21

Today I stumbled over a C riddle that got a new surprise for me.

I didn't think that -1[p] in the example below would compile, but it did. In fact, x ends up to be -3.

 int x; int array[] = {1, 2, 3}; int *p = &array[1]; x = -1[p]; 

I searched the internet for something like -1[pointer] but couldn't find anything. Okay, it is difficult to enter the correct search query, I admit. Who knows why -1[p] compiles and X becomes -3?

7
  • 3
    It's the same as -(p[1]) which is the same as -(array[2]). Commented Aug 14, 2019 at 17:22
  • 7
    The riddle has two levels: to understand it, first you must know that a[5] == 5[a]. But if you haven't also memorized C's less-than-intuitive precedence rules, or if you don't remember that C treats -1 as an application of the unary minus operator (instead of treating it as a single token and an intrinsically negative constant), you might be fooled into thinking that -1[p] is the same as p[-1] rather than -(p[1]). Commented Aug 14, 2019 at 17:42
  • 3
    And of course it binds lower than indexing because otherwise the sane looking -p[1] would try to negate a pointer ... Commented Aug 14, 2019 at 17:50
  • @EugeneSh. I was about to vote for the a[5] == 5[a] one as a dup, too, but it really only covers half of this question. I'd say the surprising precedence here is equally significant. Commented Aug 14, 2019 at 18:23
  • @SteveSummit I am not sure why precedence is so surprising and deserves a whole 5-answer question. Yes, it can be a bit confusing, but it is a very basic thing, especially given the results of the code execution.. Commented Aug 14, 2019 at 18:30

5 Answers 5

28

I'm the person that made this "riddle" (see my Twitter post)

So! What's up with -1[p]?

ISO C actually defines [] to be symmetrical, meaning x[y] is the same as y[x], where x and y are both expressions.

Naively, we could jump to the conclusion that -1[p] is therefore p[-1] and so x = 1, However, -1 is actually the unary minus operator applied to the constant 1, and unary minus has a lower precedence than []

So, -1[p] is -(p[1]), which yields -3.

This can lead to funky looking snippets like this one, too:

sizeof(char)["abc"] /* yields 'b' */

Sign up to request clarification or add additional context in comments.

Comments

12

First thing to figure out is the precedence. Namely [] has higher precedence than unary operators, so -1[p] is equal to -(1[p]), not (-1)[p]. So we're taking the result of 1[p] and negating it.

x[y] is equal to *(x+y), so 1[p] is equal to *(1+p), which is equal to *(p+1), which is equal to p[1].

So we're taking the element one after where p points, so the third element of array, i.e. 3, and then negating it, which gives us -3.

Comments

10

According to the C Standard (6.5.2 Postfix operators) the subscript operator is defined the following way

postfix-expression [ expression ] 

So before the square brackets there shall be a postfix expression.

In this expression statement

x = -1[p]; 

there is used the postfix expression 1 (that is at the same time a primary expression), the postfix expression 1[p] (that is the subscript operator) and the unary operator - Take into account that when the compiler splits a program into tokens then integer constants are considered as tokens themselves without the minus. minus is a separate token.

So the statement can be rewritten like

x = -( 1[p] ); 

because a postfix expression has a higher priority than an unary expression.

Let's consider at first the postfix sub-expression 1[p]

According to the C Standard (6.5.2.1 Array subscripting)

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).

So this sub-expression evaluates like *( ( 1 ) + ( p ) ) and is the same as *( ( p ) + ( 1 ) ).

Thus the above statement

x = -1[p]; 

is equivalent to

x = -p[1]; 

and will yield -3, because the pointer p points to the second element of the array due to the statement

int *p = &array[1]; 

and then the expression p[1] yields the value of the element after the second element of the array. Then the unary operator - is applied.

Comments

7

This

int array[] = {1, 2, 3}; 

looks like

array[0] array[1] array[2] -------------------------- | 1 | 2 | 3 | -------------------------- 0x100 0x104 0x108 <-- lets assume 0x100 is base address of array array 

Next when you do like

int *p = &array[1]; 

the integer pointer p points to address of array[1] i.e 0x104. It looks like

array[0] array[1] array[2] -------------------------- | 1 | 2 | 3 | -------------------------- 0x100 0x104 0x108 <-- lets assume 0x100 is base address of array | p holds 0x104 

And when you do like

x = -1[p] 

-1[p] is equivalent to -(1[p]) i.e -(p[1]). it looks like

-(p[1]) ==> -(*(p + 1*4)) /* p holds points to array[1] i.e 0x104 */ ==> -(*(0x104 + 4)) ==> -(*(0x108)) ==> value at 0x108 is 3 ==> prints -3 

Comments

2

What happens here is really interesting.

p[n] means *(p+n). Thats why you see 3, because "p" points to array[1] which is 2, and -p[1] is interpreted as -(*(p+1)) which is -3.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.