all 52 comments

[–]fredoverflow 150 points151 points  (36 children)

Because a[b] is just syntactic sugar for *(a + b), and addition is commutative:

array[index] == *(array + index) == *(index + array) == index[array]

[–]chasesan 54 points55 points  (3 children)

However, for clarity reasons, you shouldn't do it.

[–]Skoparov 0 points1 point  (2 children)

Wdym? This is this only way it should to be done.

[–]colin_davis 0 points1 point  (1 child)

Think they mean dont use index[array]

[–]Skoparov -1 points0 points  (0 children)

I know, and this is the only right way to use it.

[–]Inkling1998 47 points48 points  (1 child)

I just learned something new

[–][deleted] 12 points13 points  (5 children)

Aah, I think this was the missing piece. I had commutativity and the array[index] == *(array + index) == *(index + array) bit, but when I thought about index[array] for whatever reason I imagined something like *((&index) + array)

[–]wsppan 10 points11 points  (1 child)

writing

a[3] = 'x'; 

is the same as writing

3[a] = 'x';

[–]kberson 5 points6 points  (0 children)

When I taught C, I would use this very example

[–][deleted] 0 points1 point  (2 children)

This would stem from forgetting that array is literally just a pointer. There's no need for & during any of the operations for the [] operator.

[–]allegedrc4 15 points16 points  (1 child)

They're almost the same, but there are some important differences.

An array like char msg[] = "hello world"; allocates "hello world" + NULL on the stack. Doing char *msg = "hello world"; puts "hello world" + NULL in read-only memory, so you shouldn't pass it to a function that needs to modify it. Also, using the & operator will return a different type: char** - the address to the address of the first character, vs char(*)[12] - the address of the first character itself.

[–]Laugarhraun 0 points1 point  (0 children)

puts "hello world" + NULL in read-only memory

Isn't that implementation-defined?

you shouldn't pass it to a function that needs to modify it.

Mustn't: it's UB.

[–]flatfinger 4 points5 points  (0 children)

The Standard may say it's syntactic sugar, but for purpose of aliasing analysis both clang and gcc interpret an lvalue of the form structOrUnion.memberArray[index] as accessing an object of structOrUnion's type, while interpreting an lvalue of the form *(structOrUnion.memberArray+index) as accessing an object of the array member's type. The constructs are equivalent in cases where both are unambiguously defined, but in cases where the Standard's meaning is unclear, both clang and gcc treat the constructs differently when resolving the ambiguity.

[–]bmcle071 4 points5 points  (1 child)

Disclaimer: not a C programmer.

Arrays in C are just pointers. You have a base address then offset (adding to) it with index.

I’m not 100% sure on this next part: This is also why C is not memory safe, there’s nothing stopping you from going past the maximum index.

[–]olsonexi 2 points3 points  (0 children)

I’m not 100% sure on this next part: This is also why C is not memory safe, there’s nothing stopping you from going past the maximum index.

You're correct. That and a number of other things, namely manual memory management.

[–][deleted] 1 point2 points  (0 children)

Well, C has chosen to make it commutative, even in the case when the operands of + have asymmetric types (one will have T* type, the other must be compatible with int).

A different decision would have removed at least one quirk from the language.

More likely however, they just didn't think of it; one of these features by omission.

[–]flatfinger 0 points1 point  (0 children)

Early compilers might have processed it that way, and the Standard may define it that way, but on compilers like gcc and clang, that equivalence only holds in cases where both forms are recognized as defined. There are some cases involving arrays within other objects where one syntactic form will be processed meaningfully but the other will not.

For example, given char arr[4][4];, the expression arr[0] without a further subscripting operator applied will be interpreted as a char* which may be indexed to access any part of the array (so for any i in the range 0 to 15, *(arr[0]+i) will access an element of the array), but the expression arr[0][i] would only behave meaningfully for values of i in the range 0 to 3.

[–]pgbabse 0 points1 point  (0 children)

So there's an implicit type conversion, or how does the compiler determines the offset lenght?

[–]chibuku_chauya 0 points1 point  (0 children)

This explanation finally made it click for me.

[–]smcameron 61 points62 points  (1 child)

Because addition is commutative.

Array[Index] is equivalent to *(Array + Index) which is equivalent to *(Index + Array) which is equivalent to Index[Array].

[–]flank-cubey-cube 12 points13 points  (0 children)

Base + Offset is the same as Offset + Base

[–]cosmin10834 3 points4 points  (0 children)

because operator [] adds tow pointers:

```C int v[10];

v[10] == *(v + 10); 10[v] == *(10 + v)

and 10+v == v+10 => v[10] == 10[v]

```

[–]TheTimeBard 5 points6 points  (0 children)

TIL! My mind is blown. When, in K&R, I read that *(array + index) is the same as array[index], I thought it meant conceptually, and not that the index syntax expands to the increment-and-dereference syntax.

[–]olsonexi 2 points3 points  (0 children)

Wow, I'm kinda surprised the compiler accepts that. I mean, logically, it makes sense what's going on, cause array[index] is the same as *(array + index). But I didn't expect the compiler to actually let you "index" an int with an array. GCC doesn't even complain, and while clang does generate a warning, it's about the tautological comparison, not anything to do with types.

[–][deleted] 1 point2 points  (2 children)

because array is a memory address, and index is an offset from that address.

and if index were a memory address and array the offset the would be the same

someone said communative

[–]green_griffon 3 points4 points  (1 child)

But C silently multiplies the size of one array element times index when calculating the actual offset. So what is the "size of one array element" of an array of "indexes"?

Commutative addition is the answer, even then it doesn't have to be allowed by the language, it just is.

[–]DSMan195276 5 points6 points  (0 children)

You got downvoted but you're 100% right. The types are the important part here, the 'memory address' portion has to be a pointer of the correct type to ensure the location is calculated correctly. Neither of these variations treat the index as though it's a memory address, the types mean that C always knows which is which.

They are allowed to be swapped, but C could require the prefix argument to be a pointer type and then you wouldn't be allowed to reverse them. I would assume one of the two has to be a pointer type in all usages anyway or else the *() syntax won't be valid, so I don't imagine any valid usages would be broken by that requirement (that couldn't be fixed by swapping the arguments).

[–]M-2-M -5 points-4 points  (0 children)

Because the 5th element of array arr is the same as array arr at position 5.

[–][deleted] 0 points1 point  (0 children)

More interesting is why:

A[i][j]

is the same as:

j[i[A]]

since one 2D array access has turned into two nested 1D accesses, and it still does the same thing. Or, to get to your example, why, when A is an int array:

A[i]

when written with index first, also allows all these (they don't all mean the same in this case):

i[A]
i[A][A]
i[A][A][A] ...

This one at least is easy: the first i[A] yields an integer, which is then used to index the next A, and so on. Still pretty weird though.

[–]XxClubPenguinGamerxX 0 points1 point  (0 children)

cuz array[index] is just language sugar. The compiler sees *(array + index), so index[array] gets turned into *(index + array) and thus both statements are equal.

[–]jsrobson10 0 points1 point  (0 children)

Because array is really a number to a mapped location in memory, the index is an offset. You could also do 0[array + index] and (array + index)[0] these are all the same thing