Сегодня я наткнулся на некоторый код, который демонстрирует другое поведение на
clang ++ (3.7-git), g ++ (4.9.2) и Visual Studio 2013. После некоторого сокращения
Я придумал этот фрагмент, который выдвигает на первый план проблему:
#include <iostream>
using namespace std;
int len_ = -1;
char *buffer(int size_)
{
cout << "len_: " << len_ << endl;
return new char[size_];
}
int main(int argc, char *argv[])
{
int len = 10;
buffer(len+1)[len_ = len] = '\0';
cout << "len_: " << len_ << endl;
}
g ++ (4.9.2) дает такой вывод:
len_: -1
len_: 10
Таким образом, g ++ оценивает аргумент для буферизации, затем сам буфер (..) и после этого он оценивает аргумент индекса для оператора массива. Интуитивно это имеет смысл для меня.
clang (3.7-git) и Visual Studio 2013 оба дают:
len_: 10
len_: 10
Я полагаю, что clang и VS2013 оценивают все возможное, прежде чем он попадет в буфер (..). Это имеет менее интуитивный смысл для меня.
Я предполагаю, что суть моего вопроса заключается в том, является ли это явным случаем неопределенного поведения.
Редактировать: Спасибо за разъяснение, и неопределенное поведение является термином, который я должен был использовать.
Это неуточненное поведение, len_ = len
является неопределенно последовательность в отношении исполнения тела buffer()
, это означает, что один будет выполнен раньше другого, но не указано, какой порядок, но есть порядок, поэтому оценки не могут перекрываться, поэтому нет неопределенного поведения. Это означает gcc
, clang
а также Visual Studio
все правильно. С другой стороны непоследовательные оценки учитывают перекрывающиеся оценки, которые могут привести к неопределенному поведению, как отмечено ниже.
От проект стандарта C ++ 11 раздел 1.9
[Intro.execution]:
[…] Каждая оценка в вызывающей функции (включая другие вызовы функций), которая не является специально определенной
последовательность до или после выполнения тела вызываемой функции неопределенно
уважение к выполнению вызываемой функции. […]
а также неопределенно последовательность накрывается немного перед этим и говорит:
[…] Оценки A и B имеют неопределенную последовательность, когда либо A
секвенируется до того, как B или B секвенируется до A, но не указано, какой именно. [Примечание: неопределенно
последовательные оценки не могут перекрываться, но любая из них может быть выполнена первой. —Конечная записка]
который отличается от непоследовательные оценки:
[…] Если A не упорядочен ранее
B и B не секвенируются до A, тогда A и B не секвенируются. [Примечание: выполнение непоследовательного
оценки могут совпадать. — конец примечания] […]
что может привести к неопределенному поведению (акцент мой):
За исключением отмеченных случаев, оценки операндов отдельных операторов и подвыражений отдельных
выражения не упорядочены. [Примечание: в выражении, которое оценивается более одного раза во время выполнения
программы непоследовательные и неопределенно последовательные оценки ее подвыражений не должны быть
выполняется последовательно в разных оценках. — конец примечания] Вычисления значения операндов
Оператор секвенируется перед вычислением значения результата оператора. Если побочный эффект на скаляр
объект не секвенирован относительно другого побочного эффекта на тот же скалярный объект или вычисления значения
используя значение того же скалярного объекта, поведение не определено[…]
Pre C ++ 11
Pre C ++ 11 порядок вычисления подвыражений также не определен, но он использует последовательность точек в отличие от заказа. В этом случае существует точка последовательности на входе и выходе функции, что гарантирует отсутствие неопределенного поведения. Из раздела 1.9
:
[…] Последовательность указывает на вход функции и выход функции
(как описано выше) — это функции вызовов функций, оцениваемые независимо от синтаксиса выражения, вызывающего
функция может быть.
Зависать порядок оценки
Различные варианты, сделанные каждым компилятором, могут показаться неубедительными в зависимости от вашей точки зрения и ожиданий. Предметом прибавления порядка оценки является предмет РГЭ, выпуск 158: N4228 Порядок уточнения выражений для Idiomatic C ++, который рассматривается для C ++ 17, но кажется спорным на основе реакции на опрос на эту тему. Бумага охватывает гораздо больше сложный случай от «Язык программирования C ++» 4-е издание. Который показывает, что даже те, кто имеет большой опыт в C ++, могут быть запутаны
Ну, нет, это не случай неопределенного поведения. Это случай неопределенного поведения.
Не указано, является ли выражение len_ = len
будет оцениваться до или после buffer(len+1)
, Исходя из того, что вы описали, g ++ оценивает buffer(len+1)
во-первых, и Clang оценивает len_ = len
первый.
Обе возможности верны, поскольку порядок вычисления этих двух подвыражений не определен. Оба выражения будут оценены (поэтому поведение не будет определяться как неопределенное), но стандарт не определяет порядок.