Categories
arrays c pointer-arithmetic pointers

With arrays, why is it the case that a[5] == 5[a]?

1762

As Joel points out in Stack Overflow podcast #34, in C Programming Language (aka: K & R), there is mention of this property of arrays in C: a[5] == 5[a]

Joel says that it’s because of pointer arithmetic but I still don’t understand. Why does a[5] == 5[a]?

14

  • 58

    would something like a[+] also work like *( a++) OR *(++a) ?

    – Egon

    May 13, 2010 at 16:14

  • 54

    @Egon: That’s very creative but unfortunately that’s not how compilers work. The compiler interprets a[1] as a series of tokens, not strings: *({integer location of}a {operator}+ {integer}1) is the same as *({integer}1 {operator}+ {integer location of}a) but is not the same as *({integer location of}a {operator}+ {operator}+)

    – Dinah

    May 13, 2010 at 17:24

  • 15

    An interesting compound variation on this is illustrated in Illogical array access, where you have char bar[]; int foo[]; and foo[i][bar] is used as an expression.

    Oct 17, 2012 at 6:38

  • 7

    @EldritchConundrum, why do you think ‘the compiler cannot check that the left part is a pointer’? Yes, it can. It’s true that a[b] = *(a + b) for any given a and b, but it was the language designers’ free choice for + to be defined commutative for all types. Nothing could prevent them from forbidding i + p while allowing p + i.

    – ach

    Mar 14, 2014 at 19:46


  • 15

    @Andrey One usually expects + to be commutative, so maybe the real problem is choosing to make pointer operations resemble arithmetic, instead of designing a separate offset operator.

    Mar 18, 2014 at 10:36

2074

The C standard defines the [] operator as follows:

a[b] == *(a + b)

Therefore a[5] will evaluate to:

*(a + 5)

and 5[a] will evaluate to:

*(5 + a)

a is a pointer to the first element of the array. a[5] is the value that’s 5 elements further from a, which is the same as *(a + 5), and from elementary school math we know those are equal (addition is commutative).

37

  • 354

    I wonder if it isn’t more like *((5 * sizeof(a)) + a). Great explaination though.

    Dec 19, 2008 at 17:06

  • 111

    @Dinah: From a C-compiler perspective, you are right. No sizeof is needed and those expressions I mentioned are THE SAME. However, the compiler will take sizeof into account when producing machine code. If a is an int array, a[5] will compile to something like mov eax, [ebx+20] instead of [ebx+5]

    – mmx

    Dec 19, 2008 at 17:18


  • 14

    @Dinah: A is an address, say 0x1230. If a was in 32-bit int array, then a[0] is at 0x1230, a[1] is at 0x1234, a[2] at 0x1238…a[5] at x1244 etc. If we just add 5 to 0x1230, we get 0x1235, which is wrong.

    Dec 19, 2008 at 17:21

  • 45

    @sr105: That’s a special case for the + operator, where one of the operands is a pointer and the other an integer. The standard says that the result will be of the type of the pointer. The compiler /has to be/ smart enough.

    – aib

    Dec 23, 2008 at 2:08

  • 57

    “from elementary school math we know those are equal” – I understand that you are simplifying, but I’m with those who feel like this is oversimplifying. It’s not elementary that *(10 + (int *)13) != *((int *)10 + 13). In other words, there’s more going on here than elementary school arithmetic. The commutativity relies critically on the compiler recognizing which operand is a pointer (and to what size of object). To put it another way, (1 apple + 2 oranges) = (2 oranges + 1 apple), but (1 apple + 2 oranges) != (1 orange + 2 apples).

    – LarsH

    Dec 1, 2010 at 20:54


303

Because array access is defined in terms of pointers. a[i] is defined to mean *(a + i), which is commutative.

6

  • 53

    Arrays are not defined in terms of pointers, but access to them is.

    May 12, 2011 at 23:20

  • 9

    I would add “so it is equal to *(i + a), which can be written as i[a]“.

    Apr 5, 2013 at 22:11

  • 4

    I would suggest you include the quote from the standard, which is as follows: 6.5.2.1: 2 A postfix expression followed by an expression in square brackets [] is a subscripted designation of an element of an array object. The definition of the subscript operator [] is that E1[E2] is identical to (*((E1)+(E2))). Because of the conversion rules that apply to the binary + operator, if E1 is an array object (equivalently, a pointer to the initial element of an array object) and E2 is an integer, E1[E2] designates the E2-th element of E1 (counting from zero).

    – Vality

    Feb 17, 2015 at 21:41

  • 2

    Nitpick: It doesn’t make sense to say that “*(a + i) is commutative”. However, *(a + i) = *(i + a) = i[a] because addition is commutative.

    Oct 13, 2019 at 22:18


  • 1

    @AndreasRejbrand OTOH + is the only binary operator in the expression, so it’s rather clear what can be commutative at all.

    – U. Windl

    Nov 4, 2020 at 13:03

268

I think something is being missed by the other answers.

Yes, p[i] is by definition equivalent to *(p+i), which (because addition is commutative) is equivalent to *(i+p), which (again, by the definition of the [] operator) is equivalent to i[p].

(And in array[i], the array name is implicitly converted to a pointer to the array’s first element.)

But the commutativity of addition is not all that obvious in this case.

When both operands are of the same type, or even of different numeric types that are promoted to a common type, commutativity makes perfect sense: x + y == y + x.

But in this case we’re talking specifically about pointer arithmetic, where one operand is a pointer and the other is an integer. (Integer + integer is a different operation, and pointer + pointer is nonsense.)

The C standard’s description of the + operator (N1570 6.5.6) says:

For addition, either both operands shall have arithmetic type, or one
operand shall be a pointer to a complete object type and the other
shall have integer type.

It could just as easily have said:

For addition, either both operands shall have arithmetic type, or the left
operand shall be a pointer to a complete object type and the right operand
shall have integer type.

in which case both i + p and i[p] would be illegal.

In C++ terms, we really have two sets of overloaded + operators, which can be loosely described as:

pointer operator+(pointer p, integer i);

and

pointer operator+(integer i, pointer p);

of which only the first is really necessary.

So why is it this way?

C++ inherited this definition from C, which got it from B (the commutativity of array indexing is explicitly mentioned in the 1972 Users’ Reference to B), which got it from BCPL (manual dated 1967), which may well have gotten it from even earlier languages (CPL? Algol?).

So the idea that array indexing is defined in terms of addition, and that addition, even of a pointer and an integer, is commutative, goes back many decades, to C’s ancestor languages.

Those languages were much less strongly typed than modern C is. In particular, the distinction between pointers and integers was often ignored. (Early C programmers sometimes used pointers as unsigned integers, before the unsigned keyword was added to the language.) So the idea of making addition non-commutative because the operands are of different types probably wouldn’t have occurred to the designers of those languages. If a user wanted to add two “things”, whether those “things” are integers, pointers, or something else, it wasn’t up to the language to prevent it.

And over the years, any change to that rule would have broken existing code (though the 1989 ANSI C standard might have been a good opportunity).

Changing C and/or C++ to require putting the pointer on the left and the integer on the right might break some existing code, but there would be no loss of real expressive power.

So now we have arr[3] and 3[arr] meaning exactly the same thing, though the latter form should never appear outside the IOCCC.

13

  • 16

    Fantastic description of this property. From a high level view, I think 3[arr] is an interesting artifact but should rarely if ever be used. The accepted answer to this question (<stackoverflow.com/q/1390365/356>) which I asked a while back has changed the way I’ve thought about syntax. Although there’s often technically not a right and wrong way to do these things, these kinds of features start you thinking in a way which is separate from the implementation details. There’s benefit to this different way of thinking which is in part lost when you fixate on the implementation details.

    – Dinah

    Aug 24, 2013 at 1:01

  • 3

    Addition is commutative. For the C standard to define it otherwise would be strange. That’s why it could not just as easily said “For addition, either both operands shall have arithmetic type, or the left operand shall be a pointer to a complete object type and the right operand shall have integer type.” – That wouldn’t make sense to most people who add things.

    – iheanyi

    Apr 21, 2014 at 17:54

  • 12

    @iheanyi: Addition is usually commutative — and it usually takes two operands of the same type. Pointer addition lets you add a pointer and an integer, but not two pointers. IMHO that’s already a sufficiently odd special case that requiring the pointer to be the left operand wouldn’t be a significant burden. (Some languages use “+” for string concatenation; that’s certainly not commutative.)

    Apr 21, 2014 at 18:13

  • 3

    @supercat, That’s even worse. That would mean that sometimes x + 1 != 1 + x. That would completely violate the associative property of addition.

    – iheanyi

    Oct 21, 2014 at 16:34

  • 4

    @iheanyi: I think you meant commutative property; addition is already not associative, since on most implementations (1LL+1U)-2 != 1LL+(1U-2). Indeed, the change would make some situations associative which presently aren’t, e.g. 3U+(UINT_MAX-2L) would equal (3U+UINT_MAX)-2. What would be best, though, is for the language to have add new distinct types for promotable integers and “wrapping” algebraic rings, so that adding 2 to a ring16_t which holds 65535 would yield a ring16_t with value 1, independent of the size of int.

    – supercat

    Oct 21, 2014 at 16:46