Понимание оценки выражений, содержащих операторы ++ и `-> в C - PullRequest
2 голосов
/ 22 апреля 2009

Рассмотрим этот пример:

struct {
    int num;
} s, *ps;

s.num = 0;
ps = &s;
++ps->num;

printf("%d", s.num); /* Prints 1 */

Он печатает 1.
Итак, я понимаю, что это потому, что в соответствии с приоритетом операторов, -> выше, чем ++, поэтому сначала выбирается значение ps->num (равное 0), а затем оператор ++ работает с ним, поэтому он увеличивается это к 1.

struct {
    int num;
} s, *ps;

s.num = 0;
ps = &s;
ps++->num;

printf("%d", s.num); /* Prints 0 */

В этом примере я получаю 0, и я не понимаю, почему; объяснение первого примера должно быть таким же для этого примера. Но кажется , что это выражение оценивается следующим образом:
Сначала оператор ++ работает, и он работает на ps, поэтому он увеличивает его до следующего struct. Только тогда -> работает и ничего не делает, потому что просто выбирает поле num следующего struct и ничего с ним не делает.
Но это противоречит приоритетности операторов, которые говорят, что -> имеют более высокий приоритет, чем ++.

Может кто-нибудь объяснить это поведение?

Редактировать
После прочтения двух ответов, которые ссылаются на таблицы приоритетов C ++, в которых указано, что префиксные операторы ++ / -- имеют более низкий приоритет, чем ->, я немного погуглил и нашел эту ссылку , в которой говорится что это правило применяется также к самому С. Это точно и полностью объясняет это поведение, но я должен добавить, что таблица в этой ссылке противоречит таблице в моей собственной копии K & R ANSI C. Поэтому, если у вас есть предложения относительно того, какой источник является правильным, я хотел бы знать.

Спасибо.

Ответы [ 8 ]

4 голосов
/ 22 апреля 2009

Даже если бы ++ имел более высокий приоритет, это не изменило бы значение -> работает, так как это постинкремент. Посмотрите этот код, который также имеет другой пример такого поведения:

int b = 5;
int a = b++ * 3;
b = 5;
int c = (b++) * 3;

printf("%i, %i, %i\n", a, b, c); // Prints 15, 6, 15

struct {
  int num;
} s, *ps;

s.num = 35;
ps = &s;
printf("%p\n", ps); // Prints the pointer
printf("%i\n", ps++->num); // Prints 35
printf("%p\n", ps); // Prints the increased pointer

printf("%d\n", s.num); /* Prints 35 */
4 голосов
/ 22 апреля 2009

Постинкремент (ps++) и предварительный инкремент (++ps) имеют различную ассоциативность в C. Первые ассоциируются слева направо, а вторые - справа налево. Проверьте эту страницу (хотя это для C ++, поэтому приоритеты могут вводить в заблуждение).

В последнем примере вы меняете указатель на один за концом &s. Вы не изменили стоимость pointee. Если вам нужно увеличить num, вам нужно привязать ++ к num.

Подробное объяснение:

 ps++->num;

(Гипотетический) компилятор, увидев это выражение, может поместить объект ps в стек, за которым следует оператор ++, оператор -> и, наконец, объект - num. Во время оценки, где должен начинаться компилятор? Он смотрит на доступные операторы, то есть ++ и ->. Это выбирает ps++ или ps? Здесь правила приоритета: поскольку -> имеет более высокий приоритет, чем ++, требуется -> для обработки с num в качестве одного операнда и ps в качестве другого операнда. Таким образом, значение выражения становится ps->num, т. Е. 0, как вы правильно заметили. Что происходит с ps после оценки? Помните, в стеке остался другой оператор? Таким образом, компилятор применяет это к ps и теперь он указывает на один элемент после &s.

Сноска:

Стандарт ANSI / ISO C не использует грамматику приоритета оператора. Скорее он использует то, что известно как полностью факторизованная грамматика. Обычно это включает в себя точное определение грамматики, усеянное рядом нетерминалов, таких как «первичное выражение» и «выражение-смещение» и так далее. Это трудно понять, но легче для разработчика языка или поставщика компилятора. Кроме того, такая грамматика способна легко кодировать приоритеты. Тем не менее, любая полностью факторизованная грамматика совместима с грамматикой приоритета оператора, и это то, что делает большинство книг (и веб-сайтов) (а также иногда портит).

3 голосов
/ 22 апреля 2009

b = ++ a; эквивалентно:

a += 1;
b = a;

b = a ++; эквивалентно:

b = a;
a += 1;

Так что довольно ясно, почему вы не получаете результат, который ищете. То, что вы описали, будет эквивалентно (++ ps) -> num.

1 голос
/ 22 апреля 2009

ps ++ -> num увеличивает указатель ps на 1, а затем считывает данные внутри него. Поскольку ps находится сразу после s в стеке, я думаю, что указатель, скорее всего, будет указывать на себя, хотя в этом я не уверен и не важен. в основном, исходная программа делала ++ (ps-> num), но без скобок. Чтобы добиться того же, но после доступа к данным, вам придется выполнить (ps-> num) ++ или без скобок: ps-> num ++.

И поскольку ps - просто указатель, даже если вы изменили его значение, s все равно останется прежним.

1 голос
/ 22 апреля 2009

Полагаю, это потому, что они имеют разный приоритет, и в пределах одной группы они имеют особую ассоциативность (например, порядок оценки)

Отметьте здесь . Оператор postfix имеет тот же приоритет, что и разрешение указателя, но оператор префикса имеет более низкий приоритет.

1 голос
/ 22 апреля 2009

«приоритет» в основном является производным свойством; это следует из правил разбора. ++ ps-> num анализируется как ++ (ps-> num) / * (), добавленный для пояснения правил синтаксического анализа * / тогда как ps ++ -> num может быть проанализирован только как (ps ++) -> num.

0 голосов
/ 01 марта 2010

Приоритет используется для разрешения неоднозначного анализа. ++ps->num может быть проанализировано как ((++ps)->num) или (++(ps->num)); относительный приоритет ++() и -> определяет, что последний является правильным синтаксическим анализом.

Для ps++->num допустим только один разбор: ((ps++)->num), поэтому приоритет операторов не имеет значения.

0 голосов
/ 22 апреля 2009

Здесь вы можете видеть, что ++ как префикс имеет более низкий приоритет, чем ->, но как постфикс он имеет тот же приоритет, что и ->, и он оценивается слева направо, поэтому ps ++ сделано сначала, а затем ->.

Изменить: это для C ++, а не C. Так что мой ответ не верен.

...