Что такое -1 [p], когда p указывает на массив (из int) индекса?

Сегодня я наткнулся на загадку C, которая преподнесла мне новый сюрприз.

Я не думал, что -1 [p] в приведенном ниже примере будет компилироваться, но это произошло. Фактически, x оказывается -3.

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

Я поискал в Интернете что-то вроде -1 [указатель], но ничего не нашел. Ладно, я признаю, сложно ввести правильный поисковый запрос. Кто знает, почему -1 [p] компилируется, а X становится -3?


person Erik Stroeken    schedule 14.08.2019    source источник
comment
Это то же самое, что -(p[1]), то же самое, что -(array[2]).   -  person Eugene Sh.    schedule 14.08.2019
comment
Загадка состоит из двух уровней: чтобы понять ее, сначала вы должны знать, что a[5] == 5[a]. Но если вы также не запомнили менее интуитивные правила приоритета C, или если вы не помните, что C рассматривает -1 как приложение унарного оператора минус (вместо того, чтобы рассматривать его как отдельный токен и внутренне отрицательную константу ), вас могут ввести в заблуждение, если вы подумаете, что -1[p] - это то же самое, что p[-1], а не -(p[1]).   -  person zwol    schedule 14.08.2019
comment
И, конечно, он связывает ниже индексации, потому что в противном случае разумно выглядящий -p[1] попытался бы отрицать указатель ...   -  person Antti Haapala    schedule 14.08.2019
comment
@EugeneSh. Я тоже собирался проголосовать за a[5] == 5[a] как дубликат, но на самом деле он охватывает только половину этого вопроса. Я бы сказал, что удивительный приоритет здесь не менее важен.   -  person Steve Summit    schedule 14.08.2019
comment
@SteveSummit Я не уверен, почему приоритетность настолько удивительна и заслуживает целого вопроса с пятью ответами. Да, это может немного сбивать с толку, но это очень простая вещь, особенно с учетом результатов выполнения кода ...   -  person Eugene Sh.    schedule 14.08.2019
comment
@ JL2210 В отличие от более разумных языков, программисты на C действительно должны уметь разгадывать загадки такого рода, чтобы выполнять свою настоящую работу.   -  person zwol    schedule 15.08.2019
comment
@zwol Но самое меньшее, что мог бы сделать программист, - это написать -(1[p]) или -p[1], если это не было очевидно.   -  person S.S. Anne    schedule 15.08.2019


Ответы (5)


Это я придумал эту "загадку" (см. мой пост в Twitter)

Так! Что случилось с -1 [p]?

ISO C фактически определяет [] как симметричный, что означает, что x[y] совпадает с y[x], где x и y - оба выражения.

Наивно, мы могли бы сразу прийти к выводу, что -1[p], следовательно, p[-1] и, следовательно, x = 1. Однако -1 на самом деле является унарным оператором минус, применяемым к константе 1, а унарный минус имеет более низкий приоритет, чем []

Итак, -1[p] равно -(p[1]), что дает -3.

Это также может привести к появлению забавных фрагментов, подобных этому:

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

person LunarLambda    schedule 14.08.2019

Первое, что нужно выяснить - это приоритет. А именно [] имеет более высокий приоритет, чем унарные операторы, поэтому -1[p] равно -(1[p]), а не (-1)[p]. Итак, мы берем результат 1[p] и отрицаем его.

x[y] равно *(x+y), поэтому 1[p] равно *(1+p), что равно *(p+1), что равно p[1].

Итак, мы берем элемент, следующий за точкой, где p, то есть третий элемент array, то есть 3, а затем инвертируем его, что дает нам -3.

person sepp2k    schedule 14.08.2019

Согласно стандарту C (6.5.2 операторы Postfix) оператор индекса определяется следующим образом

postfix-expression [ expression ]

Поэтому перед квадратными скобками должно стоять постфиксное выражение.

В этом выражении

x = -1[p];

используется постфиксное выражение 1 (которое одновременно является первичным выражением), постфиксное выражение 1[p] (то есть оператор индексации) и унарный оператор -. Учтите, что когда компилятор разбивает программу на токены, тогда целочисленные константы считаются самими токенами без минуса. минус - это отдельный жетон.

Таким образом, заявление можно переписать как

x = -( 1[p] );

потому что постфиксное выражение имеет более высокий приоритет, чем унарное выражение.

Рассмотрим сначала постфиксное подвыражение 1[p]

Согласно стандарту C (6.5.2.1 индексирование массива)

2 Постфиксное выражение, за которым следует выражение в квадратных скобках [], является индексированным обозначением элемента объекта массива. Определение оператора индекса [] заключается в том, что E1 [E2] идентично (* ((E1) + (E2))). Из-за правил преобразования, которые применяются к бинарному оператору +, если E1 является объектом массива (эквивалентно указателем на начальный элемент объекта массива), а E2 является целым числом, E1 [E2] обозначает E2-й элемент E1 (отсчет с нуля).

Таким образом, это подвыражение оценивается как *( ( 1 ) + ( p ) ) и совпадает с *( ( p ) + ( 1 ) ).

Таким образом, приведенное выше заявление

x = -1[p];

эквивалентно

x = -p[1];

и даст -3, потому что указатель p указывает на второй элемент массива из-за оператора

int *p = &array[1];

а затем выражение p[1] возвращает значение элемента после второго элемента массива. Затем применяется унарный оператор -.

person Vlad from Moscow    schedule 14.08.2019

Этот

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

похоже

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

Далее, когда тебе нравится

int *p = &array[1];

целочисленный указатель p указывает на адрес array[1], т.е. 0x104. Это выглядит как

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

И когда тебе нравится

x = -1[p]

-1[p] эквивалентно -(1[p]), т.е. -(p[1]). это выглядит как

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

То, что здесь происходит, действительно интересно.

p [n] означает *(p+n). Вот почему вы видите 3, потому что «p» указывает на массив [1], который равен 2, а -p [1] интерпретируется как -(*(p+1)), который равен -3.

person Jordan Motta    schedule 14.08.2019