Это "* ptr ++ = * ptr + a" неопределенное поведение? - PullRequest
4 голосов
/ 02 декабря 2009

Ну, мне не очень нужен этот ответ, я просто любознательный.

Выражения типа *ptr++ = a вполне допустимы, поскольку мы работаем с двумя объектами ptr и *ptr, но если я напишу *ptr++ = *ptr + a, это все еще верно?

Например, рассмотрим следующий фрагмент:

int main(void){
   int a[] = {5,7,8,9,2};

   int* p =a;

   *p++ = 76; /*altering the first element */
   *p++ = *p + 32; /*altering the second element */    

   p = a;
   int i;
   for(i = 0;i<5; i++)
      printf("%d ",*p++);

   return 0;
}

Я думаю, что с выражением *p++ = *p + 32; не о чем беспокоиться, но я не уверен насчет задействованных точек последовательности.

Ответы [ 5 ]

12 голосов
/ 02 декабря 2009

Результат *ptr++ = *ptr + a не определен. Знак равенства не является точкой последовательности, поэтому повторное использование значения ptr в этом выражении приводит к неопределенному поведению. Просто рассмотрите результат, если ptr увеличивается до вычисления выражения RHS, и сравните его со случаем, когда ptr увеличивается после выражения RHS.

Примечание: это не означает, что результатом выражения будет любой из этих двух сценариев. Не определено не определено.

По сути, вы можете рассчитывать только на две вещи: что постинкремент оценивается некоторое время между последней точкой последовательности и следующей, и что выражение ptr++ возвращает значение ptr перед увеличивается. Так что *ptr++ = a хорошо, потому что вы можете рассчитывать на результат ptr++.

8 голосов
/ 02 декабря 2009

Сначала давайте предположим, что «p» является типом указателя.
В противном случае все операции являются просто синтаксическим сахаром для вызовов функций.

Позволяет нам разбить утверждение на части.

int* p = a;

*p++ = *p + 32;

<< Sequence Point >>
// Part 1: p++
// Note the definition of post increment in the standard is (5.2.6)
// The result of the expression p++ is the value of 'p' while the value of the 
// objects represented by 'p' is incremented. This can be represented in pseudo code as:
(A) int*  p1 = p;
(B) p = p + 1;

// Part 2: *p (On the result of Part 1) (On *p++)
(C) int& p2 = *p1;  // Note the use of p1;

// Part 3: *p (On *p + 32)
// Note: There is no linkage between this use of 'p' and the 'p' in Part 1&2
(D) int& p3 = *p;

// Part 4: *p + 32;
(E) int p5 = p3 + 32; // Note the use of p3;

// Part 5: Assignment.
(F) p2 = p5;
<< Sequence Point >>

Ordering that must be preserved:
(A) Before (B)
(A) Before (C)
(D) Before (E)
(C) Before (F)
(E) Before (F)

Учитывая вышеуказанные ограничения:
Компилятор может переупорядочить эти инструкции несколькими способами,
Но главное, на что следует обратить внимание, это то, что (B) может произойти где угодно, единственное ограничение на (B) состоит в том, что это происходит после (A). Таким образом, значение p3, как определено в (D), может быть одним из двух различных значений в зависимости от точное положение (B).

Поскольку значение p3 не может быть определено здесь.
Полученный оператор имеет неопределенное поведение.

1 голос
/ 02 декабря 2009

В терминах C, *ptr++ = *ptr + 32 будет неопределенным для 6.5.2: «Если побочный эффект на скалярном объекте не секвенирован относительно другого побочного эффекта на том же скалярном объекте или вычисления значения с использованием значения тот же скалярный объект, поведение не определено. " Вы оба модифицируете и пытаетесь использовать значение ptr в другом вычислении без промежуточной точки последовательности.

0 голосов
/ 02 декабря 2009

Я думаю, что это не определено. Однако я не уверен.

Но, немного более широкий стилистический момент: если выражение заставляет задуматься, не является ли оно неопределенным, возможно, это признак того, что ваш код не совсем понятен относительно его намерений и должен быть менее двусмысленным и иметь меньше очевидные зависимости от порядка оценки.

Раньше я думал, что Си действительно круто, потому что вы можете написать много очень коротких заявлений, которые делают много сумасшедших вещей. Я так больше не думаю. : -)

0 голосов
/ 02 декабря 2009

Не то же самое, если это то, что вы спрашиваете. Это будет компилироваться, хотя ...

Это допустимо - если PTR указывает на массив, а не на последнюю ячейку этого массива, поэтому увеличение PTR будет указывать на следующий объект в массиве.

* ptr ++ = * ptr + 2 совпадает с * ptr = * ptr + 2, ptr ++

...