34
#include <iostream>
using namespace std;

int main() 
{
    int arr[3] = { 10, 20, 30 };
    cout << arr[-2] << endl;
    cout << -2[arr] << endl;        
    return 0;
}

Output:

4196160
-30

Here arr[-2] is out of range and invalid, causing undefined behavior. But -2[arr] evaluates to -30. Why?

Isn't arr[-2] equivalent to -2[arr]?

msc
  • 33,420
  • 29
  • 119
  • 214
  • 3
    Just to make the question correct (remove the UB) you can defined `int *arr2 = arr + 2` and use `arr2` with -2. – Ajay Brahmakshatriya Jun 05 '17 at 10:34
  • 76
    I can't shake the feeling that you should have been able to *easily* figure this out for yourself by looking at the output. Well, at least you asked a well-presented question. – Cody Gray - on strike Jun 05 '17 at 12:40
  • 1
    @CodyGray I tweaked the question. – John Kugelman Jun 05 '17 at 18:22
  • 8
    _This question does not show any research effort_ (From the reasons to downvote) – pipe Jun 06 '17 at 04:41
  • 10
    While Chris's answer is correct, suppose if it *had* been equivalent to (-2)[arr]: why do you assume that printing -30 in that case is inconsistent with the behavior being undefined? – Ray Jun 06 '17 at 07:51
  • 1
    @Ray because that's how x86 and other non-popular architectures work, or I am missing something else? – LyingOnTheSky Jun 06 '17 at 21:50
  • 7
    @LyingOnTheSky It's *undefined* behavior. It would have been perfectly acceptable for arr[-2] to print both `4196160` and `-30`, clear the array, then replace the definition for cout::operator<< with one that summons a troop of dancing bears to explain why it's a bad idea to assume that undefined behavior will behave in the way you expect, even when it seems obvious based on the architecture. – Ray Jun 06 '17 at 22:42
  • 1
    Interestingly, in [BCPL](https://en.wikipedia.org/wiki/BCPL) which was the forerunner of `C`, array access looks like `array!i` which just added `i` to the address of the array and dereferenced it to the memory contents of that location. As such `array!i` was exactly equivalent to `i!array` so `-2!array` was a perfectly reasonable form of array access. – OldCurmudgeon Jun 07 '17 at 08:10
  • 2
    Am I the only one to wonder why this question has so many upvotes? Basically it asks "expression X1 invokes UB, now why doesn't expression X2 do exactly the same?" which clearly shows that the OP doesn't know what UB _means_! – Mr Lister Jun 07 '17 at 10:53
  • @OldCurmudgeon While the syntax has changed a little, `array[i]` and `i[array]` are still the same thing in C++, in the absence of user-defined types and `operator[]`. – Christopher Creutzig Jun 07 '17 at 11:10
  • When trying to figure out why something is not working as expected, a good first step is supposing that your initial assumptions are wrong (in this case assuming `-2[arr]` is `(-2)[arr]` and not `-(2[arr])`). That pretty quickly leads you to the explanation. – Bernhard Barker Jun 07 '17 at 12:31
  • 2
    @MrLister Well, no. While "UB and UB does not do the same" is a very flawed statement, the latter is in fact not UB, the problem is just not understanding operator precedence. Although I do wonder why a basic question about operator precedence gets this many upvotes. – Bernhard Barker Jun 07 '17 at 12:35
  • @Dukeling Let's just agree to disagree and not get swept away in a discussion about whether a question is about operator precedence if the answer happens to be "operator precedence"! – Mr Lister Jun 07 '17 at 13:08

4 Answers4

132

-2[arr] is parsed as -(2[arr]). In C (and in C++, ignoring overloading), the definition of X[Y] is *(X+Y) (see more discussion of this in this question), which means that 2[arr] is equal to arr[2].

Chris Jefferson
  • 7,225
  • 11
  • 43
  • 66
  • 45
    You just have to remember the operator precedence rules I guess. Or just don't use such confusing structures... – DeiDei Jun 05 '17 at 09:52
  • 15
    @DeiDei Not that it's good practice to do `(-2)[arr]` anyways. ;) – EKons Jun 05 '17 at 13:29
  • 46
    C/C++ order of operations is easy: `*` before `+`, for everything else, use brackets. – Yakk - Adam Nevraumont Jun 05 '17 at 17:35
  • 7
    @Yakk : With respect, rubbish. Would you really write `(arr[2]) + (arr[3])`? (on the grounds you can't remember if + is higher or lower priority than []). Or `((ptr->a)[2]) + ((ptr->a)[0]`. I also think that putting brackets around comparisons when combining with `||` or `&&` is pointless (but I accept that is more debateable). – Martin Bonner supports Monica Jun 06 '17 at 09:05
  • 6
    @MartinBonner Pretty sure that was a joke – Joren Jun 06 '17 at 09:44
  • 1
    @Joren: It might be - but I've seen enough comments like that where they sort of meant it. – Martin Bonner supports Monica Jun 06 '17 at 09:47
  • 1
    I'm curious about this particular way to write. Is `2[arr] == arr[2]` even when `sizeof(arrayelement) != sizeof(int)`? – LordOfThePigs Jun 06 '17 at 14:08
  • 2
    @LordOfThePigs it decomposes to pointer arithmetic. `pointer + offset` = `offset + pointer` You can try it yourself with different `arrayelement`s, e.g. use `char arrayc[]` and an `int arrayi[]` and observe the output. The 'hidden' `sizeof` multiple to the offset is handled transparrently by the compiler. – Baldrickk Jun 06 '17 at 15:19
68

The compiler parses this expression

-2

like

unary_minus decimal_integer_literal

That is definitions of integer literals do not include signs.

In turn the expression

2[arr]

is parsed by the compiler as a postfix expression.

Postfix expressions have higher precedence than unary expressions. Thus this expression

-2[arr]

is equivalent to

- ( 2[arr] )

So the unary minus is applied to the lvalue returned by the postfix expression 2[arr].

On the other hand if you wrote

int n = -2;

and then

n[arr]

then this expression would be equivalent to

arr[-2]
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • 9
    How the hell is this possible in c++? since when is `a[b]` equal to `b[a]`, I never knew that and why would one want to use this? Where can I read up on this as I cannot determine how this is called – Gizmo Jun 05 '17 at 14:15
  • 21
    @Gizmo While, contrary to some statements elsewhere, arrays DO exist as a separate data structure in C, ways to access data *within* arrays are basically limited to making use of their feature of decaying to pointers. So when you refer to `a` in most contexts you really mean "a pointer to the first element of array a". So to get the actual element you want `*a`. To get the second element, you want `*(a+1)`. This is a rather ugly and clumsy structure, so C provides `a[1]` as syntactic sugar for the above. Since `a+1` == `1+a`, `1[a]` == `a[1]`. In C++ you can overload this to be more confusing. – Muzer Jun 05 '17 at 14:32
  • 1
    Ah so that's where it comes from, also found [this](https://stackoverflow.com/questions/5073350/accessing-arrays-by-indexarray-in-c-and-c) which explains it nicely and ultimately leads to [this](https://stackoverflow.com/questions/75538/hidden-features-of-c) which was a even nicer read – Gizmo Jun 05 '17 at 14:38
  • 9
    @Gizmo This question has some more info [With arrays, why is it the case that a\[5\] == 5\[a\]?](https://stackoverflow.com/questions/381542/with-arrays-why-is-it-the-case-that-a5-5a) – Dmiters Jun 05 '17 at 17:06
18

-2[arr] is equivalent to -(2[arr]), which is equivalent to -arr[2]. However, (-2)[arr] is equivalent to arr[-2].

This is because E1[E2] is identical to (*((E1)+(E2)))

John Kugelman
  • 349,597
  • 67
  • 533
  • 578
nalzok
  • 14,965
  • 21
  • 72
  • 139
  • I thought `A[B]` was identical to `(*A+(B))`, not `(*(A)+(B))`. Otherwise `-2[arr]` would be equivalent to `(*(-2)+(arr))` which it isn't. – wizzwizz4 Jun 05 '17 at 17:35
  • 9
    @wizzwizz4 The answer is correct. `-2[arr]` is not of the form `A[B]`, because of precedences. As it parses like `-(2[arr])`, the parens break your `A` and your argument doesn't apply. If it did, then you could claim that `2*3+4` equals to `2*4+3` because of `3+4` equaling `4+3`. – maaartinus Jun 05 '17 at 23:02
  • @jamesqf @jamesqf Because there is no syntax error: `-2[arr]` is _by definition_ `-(*((2)+(arr)))`, i.e. `-(*(arr-2))`(, which is by definition `arr[-2]`). However, while `x[arr]` is a legitimate expression, normally we just write `arr[x]`. – nalzok Jun 06 '17 at 04:56
  • 1
    @SunQingyao The two statements after your i.e. are wrong. Should be -(*(arr+2)) which is -arr[2]. – wizzwizz4 Jun 06 '17 at 06:34
  • @wizzwizz4 Ah yes you're right! I was too careless :( – nalzok Jun 06 '17 at 08:39
  • @Sun Qingyao: My question was a little deeper. It's why x[arr] should be a legitmate expression. I can't see any possible use for that syntax, other than competing in obfuscated C/C++ contests. – jamesqf Jun 07 '17 at 18:46
  • @jamesqf I think the committee didn't meant to do so; it just "happens" to be a valid expression. – nalzok Jun 07 '17 at 23:48
  • 1
    @SunQingyao: The definitions would seem to suggest that `somestruct -> arr[n]` should be equivalent to `(&someStruct->arr[0])[n]`, but gcc seems to regard them differently. I'm not sure what in the Standard would justify such distinctions. – supercat Jun 21 '17 at 19:14
2

The underlying problem is with operator precedence. In C++ the [], ie the Subscript operator hold more precedence (somewhat akin to preferance) than the - unary_minus operator.

So when one writes,

arr[-2]

The compiler first executes arr[] then - , but the unary_minus is enclosed within the bounds of the [-2] so the expression is decomposed together.

In the,

-2[arr]

The same thing happens but, the compiler executes 2[] first the n the - operator so it ends up being -(2[arr]) not (-2)[arr]

Your understanding of the concept that, arr[i] i[arr] and *(i+arr) are all the same is correct. They are all equivalent expressions.

If you want to write in that way, write it as (-2)[arr]. You will get the same value for sure.

Check this out for future referance :http://en.cppreference.com/w/cpp/language/operator_precedence

msc
  • 33,420
  • 29
  • 119
  • 214
Chandrahas Aroori
  • 955
  • 2
  • 14
  • 27