2012-03-04 14 views
16

有关于C.不确定的行为其中之一是(略有修改)'a [i] = i''总是会导致明确的行为?

提出here几个有趣的问题是否未定义的行为下面这段代码的结果?

int i = 0, *a = &i; // Line 1 
a[i] = i + 1;   // Line 2 

由于没有具体答案的问题出现的这一部分,我想知道在C++中的行为,我在这里再次提高了。


规则#2从Undefined Behavior and Sequence Points

此外,该值是前一个值是唯一的访问,以确定该值将被存储

在上面的例子中

显然被访问两次:a[i](lhs)和i(rhs),并且它们中只有一个(rhs)确定要存储的值。

第2行是否违反上述规则并导致C++ 03中的未定义行为?


有一些混乱至于i是否在第2行修改?

Yes it is modified!

回答

18

这将导致C++ 03中的未定义行为以及C++ 11中明确定义的行为。

C++ 03:未定义Behvaior

从C++ 03标准,第5第4段:

之前和下一序列点标量对象应具有其之间的存储通过评估表达式最多修改一次值。此外,只有在确定要存储的值时才能访问先前值。

请注意第二句:以前的值i只能用于确定要存储的值。但在这里它也用于确定数组索引。因此,因为此作业将修改为i,因此a[0] = i+1已定义明确,而a[i] = i+1则不是。请注意,赋值不会生成一个序列点:只有完整表达式(分号)的结束。


C++ 11:明确界定行为

C++ 11摆脱了序列点的概念,而是定义了评估是其前测序。

从标准,部分1.9第15段:

操作者的操作数的值的计算操作的结果的值计算之前进行测序。如果对标量对象的副作用相对于同一标量对象的另一副作用或使用同一标量对象的值进行值计算而言是不确定的,则行为是未定义的。

赋值运算符的两个操作数在实际赋值之前都被排序。因此a[i]i+1都会被评估,只有这样才会修改i。结果是明确的。

+4

+ 1对于C + +03和C++ 11的答案。 – 2012-03-04 17:05:58

3

int i = 0, *a = &i;

有声明之间的序列点,此处因此没有UB。但是请注意,以这种方式声明/定义变量是一个坏主意。任何正常的编码标准都会告诉你每行声明一个变量。

a[i] = i;

i以任何方式不改变,因此在这里没有任何UB。

+1

那么'a [i] = i + 1;'那么? – Lazer 2012-03-04 15:43:04

+2

@Lazer:'i + 1'不会改变'i' – 2012-03-04 15:43:56

+0

@Lazer还是不行,因为'i'还没有被修改。 – 2012-03-04 15:44:06

0

在这种情况下未定义的行为只会发生,如果你修改相同的内存地址没有修改之间的序列点。具体而言,C99规格,第6.5节/ 2状态,

之前和下一序列点之间的对象应具有由表达式的评估修饰的至多一次其 存储的值。 此外,只有在访问先前值时才能确定要存储的值。

在你的情况下,序列点之间不会修改相同的存储器地址,因此没有未定义的行为。

+2

这里'i'正在被修改而没有序列点(分号是我认为第2行中唯一的序列点) – Lazer 2012-03-04 15:58:47

0

我想指出一件事情:a[i] = i不会总是会导致明确的行为。行为在指定的情况下被很好地定义的原因是因为初始值ia

让我解释:

int i = 1, *a = &i; // Line 1, i initialized to anything other than 0 
a[i] = i + 1;   // Line 2, all of a sudden we are in buffer over/underflow 

对于i其他任何初始值,我们是从的i本身,它产生不确定的行为访问不同的内存位置。

+0

实际上当'a'指向单个整数'i'时,无论'如果'*(&i + i)= i'是UB,那么根据interjay的回答,它是) – 2012-03-04 17:00:57

2

让我们分解一下表达式a[i] = i + 1你会吗?

= -- [] -- a 
    \  \_ i 
    \ 
    \_ + -- i 
     \_ 1 

有效,a[i]&i但是请注意,无论是a[i]也不i+1修改ii仅在执行=(分配本身)时才被修改。此功能生效之前

由于任何功能的操作数需要进行评估,这实际上等同于:

void assign(int& address, int value) { address = value; } 

assign(a[i], i + 1); 

这是事实,=之处在于它是内置有些特殊和不导致函数调用,仍然对两个操作数的求值都是,实际赋值为,因此它们在i被修改之前首先被计算,而a[i](它指向i位置)被赋值。

+0

interjay的答案是什么,说只能访问前一个值才能确定要存储的值? – 2012-03-04 16:59:17

+0

这是在C++ 11中工作的方式,但不是C++ 03(尽管任何合理的C++ 03编译器都可能以这种方式执行)。看到我更新的答案。 – interjay 2012-03-04 17:06:52

+0

@interjay:啊对,我对顺序点从来没有太精明,所以我只根据我的答案在最新的标准。很好的答案,谢谢你的彻底。 – 2012-03-04 18:22:43

0

不,它不。第一线具有序列点(逗号),所以它不是未定义的行为:

int i = 0, *a = &i; 

第二行是完全正常的。

a[i] = i + 1; 

由于i + 1创建一个临时值,i被修改一次,在分配。然而,这将是未定义的行为:

a[i] = i++; 
相关问题