int f() {
static int i=0;
return ++i;
}
int g() {
return f() + f();
}
Возвращает ли g()
3
или является результатом undefined
?
int f() {
static int i=0;
return ++i;
}
int g() {
return f() + f();
}
Возвращает ли g()
3
или является результатом undefined
?
6.5.2.2 Функциональные вызовы
...
10 есть точка последовательности после оценок указателя функции и фактического аргументов, но до фактического вызова. Каждая оценка в вызывающей функции (включая другие вызовы функций), которые иначе не секвентируются до или после выполнение тела вызываемой функции неопределенно упорядочено по отношению к выполнение вызываемой функции. 94)
94) Другими словами, выполнение функций не "чередуется друг с другом"
Результат состоит в том, что между каждым ++i
существует точка последовательности, поскольку она является частью вызова функции. Таким образом, это поведение четко определено.
Действительно ли это делает то, что вы намереваетесь, другое дело. Обратите внимание, что в какой-то момент вы рискуете подписью переполнения, которая undefined. И, как указывали другие, f() - f()
может не дать результата, которого вы ожидаете (оценка в правильном направлении в этом случае не гарантируется).
Оценки двух операндов оператора + не подвержены последовательности 1.
Существует точка последовательности непосредственно перед вызовом фактической функции 2. Эта точка последовательности достаточно, чтобы отделить модификации статической переменной i, делая все выражение неопределенно секвенированным, а порядок функции вызывает неуказанный 3.
Таким образом, поведение остается определенным, и первый вызов функции g всегда будет давать 3, так как неуказанный порядок вызова функции не влияет на результат.
Определяется программа, содержащая неопределенное поведение 4.
(Все цитаты взяты из: ISO/IEC 9899: 201x)
1 (6.5 Выражения 3)
Кроме указанных
позже, побочные эффекты и вычисления значений подвыражений не имеют никакого значения.
2 (6.5.2.2 Функциональные вызовы 10)
После оценок указателя функции и фактической точки есть точка последовательности
аргументов, но до фактического вызова.
3 (5.1.2.3 Исполнение программы 3)
Оценки A и B неопределенно секвенированы, когда A секвенирован
либо до, либо после B, но не указано, что.
4 (4. Соответствие 3)
Программа, которая правильна во всех других аспектах, работающая на правильных данных, содержащая
неуказанное поведение должно быть правильной программой и действовать в соответствии с 5.1.2.3.
Нет причин для этого undefined, потому что операция +
является коммутативной и потому, что для двух операций ++
есть последовательности точек последовательности.
Стандарт C точки последовательности после полного выражения, а также перед тем, как функция будет введена в вызов функции. Поэтому результаты ++
будут полностью секвенированы. Более того, поскольку +
является коммутативным, порядок вызовов f()
не изменяет результат.
Обратите внимание, что та же логика не относится к
return f() - f();
потому что -
не является коммутативным. Результат выражения выше не указан, т.е. Компилятор, совместимый со стандартами, может разумно создавать 1
или -1
, в зависимости от порядка, в котором компилятор вызывает две функции f()
.
Нет причин для поведения undefined. Статическая переменная будет сохранена в пространстве памяти .BSS, и никакие ее копии не будут сделаны - компилятор справится с этим. Функция f()
будет вызываться дважды, последовательно. Следующий пример аналогичен:
for (int i = 0; i < 2; ++i)
g += f();
Если функция f()
была бы вызвана двумя потоками, это привело бы к поведению undefined.
Вот цитата из C11 Раздел 6.5 Параграф 2:
Если побочный эффект скалярного объекта не зависит от другого побочного эффекта для одного и того же скалярного объекта или вычисления значения с использованием значения одного и того же скалярного объекта, поведение undefined. Если существует несколько допустимых порядков подвыражений выражения, то поведение undefined, если такой необъективный побочный эффект возникает в любом из порядков .84
Но в соответствии с разделом 5.1.2.3, параграф 3, упорядочение неопределенно упорядочено, а не не имеет последствий, поэтому первое предложение выше не применяется. Второе предложение применяется только в том случае, если в любом из упорядочений есть побочные эффекты. Но в каждом из упорядочений нет никаких побочных эффектов.
В стандарте C11 не говорится о влиянии неопределенно упорядоченных вычислений. Возможно, мы должны сделать вывод о том, что приемлемым для соответствующего компилятора является получение выходов любой из возможных последовательностей. В этой интерпретации имеется две возможные последовательности, каждая из которых приводит к g(), возвращающему значение 3.
Поэтому я считаю, что это приемлемо.
Обратите внимание, что в C99 нет раздела, соответствующего разделу 5.1.2.3 в C11. Раздел 5.5 В пункте 3 говорится, что упорядочение подвыражений и порядок, в котором происходят побочные эффекты, являются неуточненными.
"Unspecified" - это не то же самое, что "undefined".
Это заставляет меня думать, что в C99 это не поведение undefined.