Точки последовательности при вызове функций в C и неопределенном/неуказанном поведении

Я пытаюсь определить свое понимание точек следования в C - просто хотел кое-что проверить. В настоящее время я считаю, что (1) не определено, тогда как (2) просто не определено, на основании того, что в (2) есть точки последовательности после оценки аргументов для g и h (поэтому мы не изменяем i дважды между точки последовательности), но порядок оценки аргументов f по-прежнему не указан. Правильно ли я понимаю?

#include <stdio.h>

int g(int i) {
    return i;
}

int h(int i) {
    return i;
}

void f(int x, int y) {
    printf("%i", x + y);
}

int main() {
    int i = 23;
    f(++i, ++i); // (1)
    f(g(++i), h(++i)); // (2)
    return 0;
}

РЕДАКТИРОВАТЬ:

Кажется, ключевым моментом здесь является то, может ли компилятор выполнять оба приращения до вызова g или h - насколько я понимаю из приведенных ниже ответов, это так, хотя я был бы признателен за подтверждение того, что это так.


person Stuart Golodetz    schedule 12.06.2012    source источник
comment
@MichaelDorgan: Не буду :) Я работаю над инструментом статического анализа, чтобы помочь людям не делать подобных вещей, и различие может иметь значение.   -  person Stuart Golodetz    schedule 12.06.2012


Ответы (2)


Нет, согласно 6.5.2.2 10 между оценкой аргументов подвыражения, непосредственно перед фактическим вызовом, нет точки последовательности.

Один из способов взглянуть на это состоит в том, что не указано, является ли поведение неопределенным; если реализация упорядочивает два подвыражения ++i перед любым вызовом g или h, то поведение не определено, но если подвыражения ++i оцениваются как можно позже (непосредственно перед вызовом g и h соответственно), то поведение не определено. Однако, поскольку реализация всегда может выбирать между любым разрешенным неопределенным поведением, общий результат не определен.

person ecatmur    schedule 12.06.2012
comment
Правильно - так что два ++i могут произойти до того, как будут вызваны g и h, и в этом случае на пути нет точки последовательности, и она будет неопределенной. - person Stuart Golodetz; 12.06.2012
comment
не указано, является ли поведение неопределенным: я думаю, что в C нет такой вещи. Здесь поведение не определено. - person ouah; 12.06.2012
comment
Я думаю, что, несмотря на терминологию, я собираюсь дать этот ответ, так как это на самом деле помогло мне больше думать об этом (хотя я проголосовал за оба ответа, поскольку они оба были полезны). Вероятно, будущим читателям было бы полезно, если бы терминологию можно было ужесточить, хотя я думаю. - person Stuart Golodetz; 13.06.2012

Неправильно. Точки последовательности определяют частичный порядок разрешенного порядка операций. В случае (2) имеются точки следования:

  1. Через точку с запятой в конце строки (1)
  2. После оценки аргументов g (т.е. ++i), но перед вызовом g
  3. После оценки аргументов h (т.е. ++i), но перед вызовом h
  4. После оценки аргументов f (т.е. после возврата f и g), но до вызова f
  5. После возвращения из f

Таким образом, частичный порядок выглядит так, сверху вниз:

    1
   / \
  /   \
 2     3
  \   /
   \ /
    4
    |
    | 
    5

2 и 3 не упорядочены друг относительно друга, так как порядок вычисления аргументов не указан. Поскольку i изменяется дважды между точками последовательности 1 и 4, поведение не определено.

person Adam Rosenfield    schedule 12.06.2012
comment
Ах да, рад, что проверил! Я знал о том, что точки последовательности являются частичным порядком, но я думал так - поскольку порядок оценки аргумента не указан, вы получаете либо 1 -> 2 -> 3 -> 4 -> 5, либо 1 -> 3 -> 2 -> 4 -> 5. В любом случае я полагал, что между 1 и 4 (либо 2, либо 3) есть точка последовательности. Кажется, я совсем упустил суть. - person Stuart Golodetz; 12.06.2012
comment
Я не возражаю против такого подхода, но думаю, что он не сразу следует из стандарта; 6.5 2 говорит, что объект может быть изменен не более одного раза между соседними точками последовательности, но 1 и 4 не являются смежными на приведенном выше графике. Должна ли реализация вести себя так, как если бы граф точек следования был линеаризован? - person ecatmur; 13.06.2012
comment
@ecatmur: Хм, хорошая мысль. Я просмотрел стандарт, и я тоже не могу понять это. C99 §6.5/2 говорит: «Между предыдущей и следующей точкой последовательности...», но неясно, какая следующая точка последовательности после #1 — порядок оценки g и h не указан, и каждая из них имеет точку последовательности после оценки их аргументов, но перед их вызовом, и эти две точки следования находятся перед #4. - person Adam Rosenfield; 13.06.2012
comment
@AdamRosenfield: В этом суть моего непонимания по этому поводу - если бы это был определенно тот случай, когда сначала оценивался весь g(++i) или весь h(++i), то в любом случае была бы точка последовательности между двумя приращениями и поведение, по-видимому, не было бы неопределенным (хотя, возможно, оно было бы в формальном смысле). Но если оба приращения могут происходить до вызова g или h, то поведение, очевидно, будет скорее неопределенным, чем неопределенным. Мое текущее понимание состоит в том, что это точка здесь. - person Stuart Golodetz; 14.06.2012
comment
@Stuart: Хотя стандарт не говорит об этом явно, мне интересно, можно ли это интерпретировать как означающее, что если существует разрешенный порядок побочных эффектов и точек следования, который приведет к тому, что объект будет изменен более одного раза между точками следования, поведение не определено? Потому что с такой формулировкой поведение было бы явно неопределенным, поскольку компилятор мог бы поставить обе ++i перед вызовом либо g, либо h (хотя это и не требуется). - person Adam Rosenfield; 15.06.2012
comment
@AdamRosenfield: Я, безусловно, согласен с тем, что если бы эта формулировка была там и если бы это было определенно так, что оба приращения могут происходить до вызова g или h, то поведение было бы явно неопределенным. Я думаю, что я также рад сделать предположение, что он не определен, только если оба приращения могут произойти первыми (т.е. я рад принять интерпретацию, которую вы даете). Что мне не ясно (и какие местные дебаты там, где я работаю), так это то, где говорится, что приращения могут происходить до того, как будет сделан один из двух вызовов. - person Stuart Golodetz; 15.06.2012