Comment by xen0

3 days ago

Reading through, something small caught me by surprise.

https://c3-lang.org/language-common/arrays/#fixed-size-multi...

Multi dimensional arrays are not declared in the same way they are accessed; the order of dimensions is reversed.

Accessing the multi-dimensional fixed array has inverted array index order to when the array was declared.

That is, the last element of 'int[3][10] x = {...}' is accessed with 'x[9][2]'.

This seems bizarre to me. What am I missing? Are there other languages that do this?

Please consider a variable `List{int}[3] x`, this is an array of 3 List{int} containing List{int}. If we do `x[1]` we will get an element of List{int}, from the middle element in the array. If we then further index this with [5], like `x[1][5]` we will get the 5th element of that list.

If we look at `int*`, the dereference will peel off the `*` resulting in `int`.

So, the way C3 types are declared is the most inside one is to the left, the outermost to the right. Indexing or dereferencing will peel off the rightmost part.

C uses a different way to do this, we place `*` and `[]` not on the type but on the variable, in the order it must be unpacked. So given `int (*foo) x[4]` we first dereference it (from inside) int[4], then index from the right.

If we wanted to extract a standalone type from this, we'd have `int(*)[4]` for a pointer to an array of 4 integers. For "left is innermost", the declaration would instead be `int[4]*`. If left-is-innermost we can easily describe a pointer to an array of int pointers (which happens in C3 since arrays don't implicitly decay) int*[4]*. In C that be "int*(*)[4]", which is generally regarded as less easy to read, not the least because you need to think of which of * or [] has priority.

That said, I do think that C has a really nice ordering to subscripts, but it was unfortunately not possible to retain it.

  • Thanks for pointing out what I was missing.

    Please consider a variable `List{int}[3] x`, this is an array of 3 List{int} containing List{int}. If we do `x[1]` we will get an element of List{int}, from the middle element in the array. If we then further index this with [5], like `x[1][5]` we will get the 5th element of that list.

    I get that motivation. In C++ it's an odd case that where `std::vector<int> x[4]` is "reversed" in a sense compared to `int x[4][100]`. And this quirk is shared with other languages (Java, C#).

    But in my experience, mixing generic datatypes like this with arrays is quite rare, and multi-dimensional array like structures with these types is often specified via nesting (`std::vector<std::vector>>`) which avoids confusion.

    The argument re. pointers is more convincing though.

File it with the footgun of the two different array slicing syntaxes: https://c3-lang.org/language-common/arrays/#slicing-arrays

I have already opened a discussion about this with the author, and I must say I agree to disagree that a language needs arr[start..end] (inclusive) as well as arr[start:len] (up to len-1) and if you use the wrong one, you’ve now lost a foot and your memory is corrupted.

  • The closed intervals for slices caught my eye as well, but I simply filed that under 'that's a weird quirk' rather than 'wtf?'.

    It would require more thinking on my end to change that to either 'this is an acceptable choice' or 'this is a terrible idea'.

    But the array indices being reversed on declaration? I cannot think of an upside to that at all.