2015-04-08 10 views
20

Сегодня я наткнулся на некоторый код, который показывает различное поведение на лязгом ++ (3,7-ГИТ), г ++ (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; 
} 

г ++ (4.9.2) дает следующий результат:

len_: -1 
len_: 10 

Так г ++ оценивает аргумент в буфер, затем буфер (..) сам и после этого он оценивает индекс аргумент оператора массива. Интуитивно это имеет смысл для меня.

лязг (3,7-мерзавец) и Visual Studio 2013 и дают:

len_: 10 
len_: 10 

Я полагаю, лязг и VS2013 оценивает все возможное, прежде чем он decends в буфер (..). Это делает меня менее интуитивным.

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

Редактировать: Спасибо, что очистили это, и неопределенное поведение - это термин, который я должен был использовать.

+3

Если кто-то сделал «буфер (len + 1) [len_ = len] = '\ 0';" Я бы сказал им изменить его :) – paulm

ответ

18

Это unspecified behavior, len_ = len является неопределенно секвенировали относительно исполнения тела buffer(), что означает, что один будет выполняться до другого, но не указано, в каком порядке, но есть порядок так оценки может не перекрываются, поэтому нет неопределенного поведения. Это означает, что gcc, clang и Visual Studio все в порядке. С другой стороны, необнуримые оценки допускают перекрывающиеся оценки, которые могут привести к неопределенному поведению, как указано ниже.

Из draft C++11 standard секции 1.9[intro.execution]:

[...] Каждая оценка в вызывающей функции (в том числе других вызовов функций), не иначе конкретно секвенировал до или после того, как выполнение тела вызываемой функции неопределенно секвенировало относительно исполнения вызываемого function.9 [...]

и неопределенно секвенировали покрыта немного перед этим и говорит:

[...] Оценки А и В, когда последовательность неопределенно либо А секвенировали перед В или В секвенировали до того, как, но это не определено который. [Примечание. Неопределенно оценки секвенирования не могут перекрываться, но либо могут быть выполнены в первую очередь. -end примечание]

, который отличается от unsequenced оценок:

[...] Если A не секвенирован до B и B не секвенированы до A, то A и B не подвержены влиянию. [Примечание. Выполнение необоснованных оценок может перекрываться. -end примечание] [...]

, который может привести к непредсказуемому поведению (курсив мой ):

За исключением особо оговоренных случаев, оценки операндами отдельных операторов и подвыражениям индивидуального выражения не подвержены изменениям. [Примечание. В выражении, которое оценивается более одного раза во время выполнения программы, необнаружированные и неопределенно упорядоченные оценки его подвыражений не обязательно должны быть , выполняемые последовательно в разных оценках. -end note] Вычисления значений операндов оператора секвенированы перед вычислением значения результата оператора. Если побочный эффект от скалярного объекта unsequenced по отношению к любому другому побочному эффекту на тот же скалярном объект или вычисления значения с использованием значения одного и тем же скалярного объектом, поведение не определен [...]

Pre C++ 11

Pre C++11 порядок оценки подвыражения также не определено, но он использует sequence points в отличие от заказа. В этом случае есть точка последовательности при входе функции и выходе функции, что гарантирует отсутствие неопределенного поведения. Из секции 1.9:

[...] Точки последовательности в функции-входа и выхода функции- (как описано выше), особенности вызовов функций, как оценивается, независимо от синтаксиса выражения, что вызывает функция бывает.

Прибивание вниз порядок оценки

Различного выбора, сделанный каждый компилятор может показаться неинтуитивным в зависимости от вашей точки зрения и ожиданий. Предметом приведения порядка оценки является предмет EWG issue 158: N4228 Refining Expression Evaluation Order for Idiomatic C++, который рассматривается для C++ 17, но кажется спорным based on the reactions to a poll on the subject. В документе содержится гораздо больше complicated case от «Язык программирования C++» 4-е издание. Который показывает, что даже те, у кого большой опыт работы на C++, могут сработать.

12

Ну, нет, это не случай неопределенного поведения. Это случай неуказанного поведения.

Не определено, будет ли выражение len_ = len оцениваться до или после buffer(len+1). Из вывода, который вы описали, g ++ сначала оценивает buffer(len+1), а clang сначала оценивает len_ = len.

Обе возможности верны, так как порядок оценки этих двух подвыражений не указан. Оба выражения будут оцениваться (поэтому поведение не может считаться неопределенным), но стандарт не определяет порядок.