множественный ++, работающий с переменными и указателями - PullRequest
1 голос
/ 12 ноября 2009

Я думаю, что оператор ++ делает

  1. a++; // a+=1 after calculating this line
  2. ++a; // a+=1 before calcuating this line

Я пытаюсь изучать указатели и думаю, что что-то неправильно понял.

int a=10;
int arr[3]={0,1,2};
int *ptr;
ptr=arr;

printf("%d,%d,%d,%d\n",a++,a++,++a,++a);
printf("%d,%d,%d\n", ptr[0],ptr[1],ptr[2]);
printf("%d,%d,%d,%d,%d,%d", * ptr++, ( * ptr)++, ++ * ptr, ++( * ptr), *++ptr, * ptr);

Я ожидал, что результат будет:

12, 12, 12, 12
0,1,2
3,3,3,3,3,3,3

Но это не так. Это было так:

13,12,14,14
0,1,2
4,3,2,2,2,2

Почему это?

Ответы [ 4 ]

6 голосов
/ 12 ноября 2009

Вы не должны делать более одного приращения аргументов функции ... потому что порядок, в котором они могут быть оценены, неоднозначен. Результат такого кода не определен.

Значение: printf("%d,%d,%d,%d\n",a++,a++,++a,++a); Должно быть записано как

a++; a++;
++a; ++a;
printf("%d, %d, %d, %d\n", a, a, a, a);

Попробуйте сначала исправить это и посмотрите, не сбивают ли результаты с толку.

В целом, вы должны иметь только один шаг между парой точек последовательности.

Редактировать: Крис прав, нет смысла писать четыре шага в середине нигде. Чтобы лучше ответить на ваш вопрос: Для функции void f(int) и void g(int), с int a=0,

f(++a) = f(1);
f(a++) = f(0);
g(++a, ++a) = g(???); // undefined!

Итак, увеличиваем не более одного раза в аргументе функции.

6 голосов
/ 12 ноября 2009

Не делай этого. Поведение не определено.

Из спецификации C (раздел 6.5) ...

Между предыдущим и следующим точка последовательности, которую должен иметь объект его сохраненное значение изменяется не более одного раза по оценке выражения. Кроме того, предыдущее значение должно быть доступ только для определения значения храниться.

За исключением случаев, обозначенных синтаксисом или указано иное позже (для оператор вызова функции (), &&, || ,?:, и операторы запятой), порядок вычисления подвыражений и порядок, в котором побочные эффекты имеют место оба не определены.

Другими словами, если вы обновляете значение переменной несколько раз в аргументах для функции, вы не пишете допустимый код на Си.

3 голосов
/ 12 ноября 2009

См. 3.2 в C FAQ:

3.2: под моим компилятором код

    int i = 7;
    printf("%d\n", i++ * i++);

отпечатков 49. Независимо от порядка оценки, не так ли печать 56?

A: Хотя операторы postincrement и postdecrement ++ и - выполнять свои операции после получения первого значения, Смысл «после» часто понимают неправильно. Это не гарантируется, что увеличение или уменьшение выполняется сразу после отказа от предыдущего значения и до любого другая часть выражения оценивается. Это просто гарантирует, что обновление будет выполнено за некоторое время до Выражение считается «законченным» (перед следующей «последовательностью» пункт "в терминологии ANSI C; см. вопрос 3.8). В Например, компилятор решил умножить предыдущее значение на сам и выполнить оба приращения позже.

Поведение кода, который содержит несколько неоднозначных сторон эффекты всегда были неопределенными. (Грубо говоря, «множественные, неоднозначные побочные эффекты» мы подразумеваем любую комбинацию операторы инкремента, декремента и присваивания в одном выражение, которое вызывает изменение одного и того же объекта дважды или модифицировано, а затем проверено. Это грубо определение; см. точный вопрос 3.8 и вопрос 11,33 для значения «неопределенный».) Даже не пытайтесь найти как ваш компилятор реализует такие вещи (вопреки опрометчивые упражнения во многих учебниках C); как мудро указывают K & R "если вы не знаете, как они выполняются на разных машинах, эта невинность может помочь защитить вас. "

Ссылки: K & R1 Sec. 2,12 стр. 50; K & R2 Sec. 2,12 стр. 54; ISO Sec. 6.3; H & S Sec. 7,12 с. 227-9; CT & P Sec. 3.7 стр. 47; PCS Sec. 9,5 с. 120-1.

2 голосов
/ 12 ноября 2009

Это не «на строку», а «на точку последовательности», которая похожа на «на выражение», что результат до и после приращения, кажется, имеет место.

Фактически, приращение всегда происходит немедленно. Единственное изменение заключается в том, приведет ли значение термина к первоначальному или последующему значению.

Чтобы полностью понять, что C не является линейно-ориентированным, обратитесь к стандарту и прочитайте части о «точках последовательности».

Строки, начинающиеся с '#', являются вводом препроцессора. Препроцессор для C ориентирован на строки, но, в противном случае, сам C считает символы переноса строки такими же, как любые другие пробелы, такие как табуляция или пробел.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...