Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/django/20.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 如果复合表达式中的多个操作数修改同一个对象,这是未定义的行为吗?_C++_C++11_Undefined Behavior_Language Lawyer_Unspecified Behavior - Fatal编程技术网

C++ 如果复合表达式中的多个操作数修改同一个对象,这是未定义的行为吗?

C++ 如果复合表达式中的多个操作数修改同一个对象,这是未定义的行为吗?,c++,c++11,undefined-behavior,language-lawyer,unspecified-behavior,C++,C++11,Undefined Behavior,Language Lawyer,Unspecified Behavior,我模模糊糊地记得在某个地方读到,如果复合表达式中的多个操作数修改同一个对象,这是一种未定义的行为 我相信下面的代码中显示了这个UB的一个示例,但是我在g++、clang++和visual studio上编译过,它们都打印出相同的值,并且似乎不能在不同的编译器中生成不可预测的值 #include <iostream> int a( int& lhs ) { lhs -= 4; return lhs; } int b( int& lhs ) { lhs *= 7; re

我模模糊糊地记得在某个地方读到,如果复合表达式中的多个操作数修改同一个对象,这是一种未定义的行为

我相信下面的代码中显示了这个UB的一个示例,但是我在g++、clang++和visual studio上编译过,它们都打印出相同的值,并且似乎不能在不同的编译器中生成不可预测的值

#include <iostream>

int a( int& lhs ) { lhs -= 4; return lhs; }
int b( int& lhs ) { lhs *= 7; return lhs; }
int c( int& lhs ) { lhs += 1; return lhs; }
int d( int& lhs ) { lhs += 2; return lhs; }
int e( int& lhs ) { lhs *= 3; return lhs; }

int main( int argc, char **argv )
{
    int i = 100;
    int j = ( b( i ) + c( i ) ) * e( i ) / a( i ) * d( i );

    std::cout << i << ", " << j << std::endl;

    return 0;
}
#包括
int a(int&lhs){lhs-=4;返回lhs;}
intb(int&lhs){lhs*=7;返回lhs;}
int c(int&lhs){lhs+=1;返回lhs;}
int d(int&lhs){lhs+=2;返回lhs;}
int e(int&lhs){lhs*=3;返回lhs;}
int main(int argc,字符**argv)
{
int i=100;
int j=(b(i)+c(i))*e(i)/a(i)*d(i);

std::coutNo。不是。这里不存在未定义的行为(假设
int
算法没有溢出):所有
i
的修改都是通过序列点隔离的(使用C++03术语)。每个函数的入口都有一个序列点,出口都有一个序列点

此处未指明该行为

<>您的代码实际上遵循与通常用来说明未定义的和未指定的行为之间的差异的经典示例相同的模式。
int i = 1;
int j = ++i * ++i;
人们通常会声称,在本例中,“结果不取决于评估顺序,因此
j
必须始终为6”。这是一个无效的声明,因为行为未定义

但是在这个例子中

int inc(int &i) { return ++i; }

int i = 1;
int j = inc(i) * inc(i);
该行为在形式上只是未指定的。也就是说,计算顺序未指定。但是,由于表达式的结果根本不取决于计算顺序,
j
保证总是以
6
结尾。这是一个示例,说明一般危险的未指定行为组合会导致性能下降明确定义的结果

在您的情况下,表达式的结果在很大程度上取决于求值顺序,这意味着结果将是不可预测的。然而,这里没有未定义的行为,即不允许程序格式化您的硬盘驱动器。它只允许在
j
中产生不可预测的结果


另外,您的表达式的一些计算场景可能会导致有符号整数溢出(我还没有全部分析过),它本身会触发未定义的行为。因此,在表达式中仍然存在导致未定义行为的未指定行为的可能性。但这可能不是您的问题所在。

不,它不是未定义的行为

但它确实调用了未指定的行为

这是因为子表达式的求值顺序未指定

int j = ( b( i ) + c( i ) ) * e( i ) / a( i ) * d( i );
在上述表达式中,子表达式:

b(i)
c(i)
e(i)
a(i)
d(i)
可以按任何顺序进行评估。因为它们都有副作用,所以结果将取决于此顺序

如果将表达式拆分为所有子表达式(这是伪代码)
然后您可以看到所需的任何顺序。不仅可以按任何顺序执行上述表达式,还可以将它们与更高级别的子表达式交错(仅使用少量构造)


不是未定义的行为,但它有未指定的结果:唯一修改的对象是通过代码传递给函数的<代码> i>代码>。但是,调用函数的顺序点(我没有C++ 2011与它们:它们被称为不同的东西)。,即表达式中的多次更改不会导致未定义的行为


但是,未指定表达式的求值顺序。因此,如果求值顺序发生更改,您可能会得到不同的结果。这不是未定义的行为:结果是所有可能的求值顺序之一。未定义的行为意味着程序可以按其所需的任何方式运行,包括生成“expected”(程序员预期)是问题表达式的结果,同时会导致所有其他数据的出现。

这会引发比UB“愚蠢的行为”更险恶的行为“.这个例子本身只是我试图制造问题,而不是试图实际做一些建设性的事情:P@Pubby:这显然是人工代码。因此,它比“愚蠢”更聪明。我想这仍然是未指定的行为?@GManNickG但函数调用之间没有序列点。如果调用多个函数,所有函数都修改同一个变量,函数调用之间没有序列点,这不是仍然是UB吗?或者至少是未指定的,因为无法保证调用哪个函数irst?@sepp2k:每次修改后都会有序列点(实际上函数本身有多个序列点)。例如,
在函数中引入序列点。此外,函数调用前后都有序列点。@David:在
b(i)+c(i)中不需要序列点
因为它本身不修改
i
。序列点存在于每个函数的入口和存在处,这确保
i
的每个实际修改都被序列点包围,从而彼此隔离。@sepp2k:删除我的注释,因为它已添加到答案中,但否:要调用一个函数,必须引入一个序列点,这意味着,由于所有修改都发生在函数中(在本例中),因此所有修改都有一个中间序列点在被调用函数体的执行之前或之后没有特别排序的函数,相对于被调用函数的执行是不确定排序的
tmp_1 = b(i)           // A
tmp_2 = c(i)           // B
tmp_3 = e(i)           // C
tmp_4 = a(i)           // D
tmp_5 = d(i)           // E

tmp_6 = tmp_1 + tmp_2  // F   (Happens after A and B)
tmp_7 = tmp_6 * tmp_3  // G   (Happens after C and F)
tmp_8 = tmp_7 / tmp_4  // H   (Happens after D and G)
tmp_9 = tmp_8 * tmp_5  // I   (Happens after E and H)

int j = tmp_9;         // J   (Happens after I)