我知道这是未定义的行为:
int i = 0;
int a[4];
a[i] = i++; //<--- UB here
因为左手边和右手边的
i
的评估顺序未定义(;
是唯一的序列点)。
进一步推理,在我看来,这将是未定义未指定的行为:
int i = 0;
int foo(){
return i++;
}
int main(){
int a[4];
a[i] = foo();
return 0;
}
尽管据我所知,
=
的右侧有一些序列点,但仍然是f()
还是a[i]
。
我的假设正确吗?当我在赋值的左侧使用全局或静态变量而右侧在任何情况下都不会修改它时,我是否必须非常小心?
a[i] = foo();
这里未指定是首先评估
foo
还是a[i]
。在新的 C++11 措辞中,这两个计算是无序的。不过,仅此一点并不会导致未定义的行为。这是当对同一个标量对象进行两次无序访问时,至少其中一次正在写入。这就是为什么 a[i] = i++;
是 UB。
这两个语句之间的区别在于,对
foo()
的调用确实引入了序列点。 C++11 的措辞有所不同:calling 函数内的执行相对于 calling 函数内的其他计算而言是不确定的顺序。
这意味着
a[i]
内的 i++
和 foo
之间存在部分排序。结果,a[0]
或 a[1]
将被设置为 0,但程序已明确定义。
a[i] = i++;
这是未定义的行为,因为
i
的值在两个序列点之间都被修改和访问(并且访问不直接参与i
的下一个值的计算)。
这也是未指定的行为,因为评估的顺序是未指定的(i
的增量可以发生在使用i
作为a
的索引之前或之后)。
当你引入函数调用时,例如:
a[i] = foo();
函数调用引入了另外两个序列点:一个在函数进入之前,一个在函数返回之后。
这意味着函数内部
i
的增量被两个序列点包围,并且不会导致未定义的行为。
无论函数调用是在使用
i
作为赋值左侧的索引之前还是之后完成,这仍然是未指定的行为。