序列点歧义,未定义的行为?

时间:2015-04-08 11:40:09

标签: c++ unspecified-behavior

今天我遇到了一些表现出不同行为的代码 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在它降到缓冲区(..)之前评估所有可能的东西。这对我来说不太直观。

我想我的问题的关键在于这是否是未定义行为的明显案例。

编辑:感谢您清除此问题,并且未指明的行为是我应该使用的术语。

2 个答案:

答案 0 :(得分:18)

unspecified behavior len_ = lenbuffer() 另外,但没有指定哪个顺序,但有一个排序,所以评价不能重叠,因此没有未定义的行为。这意味着gccclangVisual Studio都是正确的。另一方面,未经测试的评估允许重叠评估,这可能导致未定义的行为,如下所述。

来自draft C++11 standard部分1.9 [intro.execution]

  

[...]调用函数中的每个评估(包括其他函数调用)都没有特别说明   在执行被调用函数体之前或之后对序列进行测序是不确定的   关于被调用函数的执行。[...]

不确定地排序在此之前有所涉及,并说:

  

[...]当A或A时,评估A和B都是不确定的   在A或B之前对B或B进行测序之前对其进行测序,但未指定哪一种。 [注意:不确定   有序的评估不能重叠,但可以先执行。 - 后注]

未经测试的评估不同

  

[...]如果A之前没有排序   B和B在A之前未被测序,然后A和B未被测序。 [注:执行无序   评估可以重叠。 - 尾注] [...]

可导致未定义的行为(强调我的):

  

除非另有说明,否则对个体操作员的操作数和个体的子表达式进行评估   表达式没有排序。 [注意:在执行期间多次计算的表达式中   一个程序,对其子表达式的无序和不确定顺序的评估不一定​​是   在不同的评估中表现一致。 -end note]一个操作数的值计算   在运算符结果的值计算之前对运算符进行排序。 如果对标量有副作用   对于相同标量对象的另一个副作用或值计算,对象未被排序   使用相同标量对象的值,行为未定义 [...]

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来自&#34; C ++编程语言&#34;第4版。这表明即使是那些具有丰富C ++经验的人也会被绊倒。

答案 1 :(得分:12)

嗯,不,这不是未定义行为的情况。这是一个未指明行为的案例。

未指定是否在len_ = len之前或之后评估表达式buffer(len+1)。从您描述的输出中,g ++首先评估buffer(len+1),然后clang首先评估len_ = len

两种可能性都是正确的,因为这两个子表达式的评估顺序是未指定的。将评估两个表达式(因此行为不符合未定义的条件),但标准未指定顺序。