What is '-1[p]' when p points to an array (of int) index? [duplicate]
Asked Answered
M

5

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?

Mercantile answered 14/8, 2019 at 17:18 Comment(7)
It's the same as -(p[1]) which is the same as -(array[2]).Singapore
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]).Fractionate
And of course it binds lower than indexing because otherwise the sane looking -p[1] would try to negate a pointer ...Ry
@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.Masticatory
@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..Singapore
@JL2210 Unlike how it is in more sensible languages, C programmers actually need to be able to solve these kinds of riddles in order to do their actual jobs.Fractionate
@Fractionate But the least a programmer could do would be to write -(1[p]) or -p[1] if it wasn't obvious.Ferdy
P
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' */

Prosser answered 14/8, 2019 at 17:47 Comment(0)
B
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.

Bowra answered 14/8, 2019 at 17:23 Comment(0)
U
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.

Ubana answered 14/8, 2019 at 17:41 Comment(0)
P
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
Pronation answered 14/8, 2019 at 17:23 Comment(0)
P
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.

Piero answered 14/8, 2019 at 17:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.