提问人:D.J. Elkind 提问时间:11/1/2023 最后编辑:Peter CordesD.J. Elkind 更新时间:11/1/2023 访问量:155
如果 func() 修改全局变量 [duplicate],int sum = func(1) + func(2) 是否会导致未定义的行为
Does int sum = func(1) + func(2) cause undefined behavior if func() modifies a global variable [duplicate]
问:
受这篇 SO 帖子的启发,我想知道下面的代码片段是否会导致 UB 同时发生,并且可以同时修改并按未指定的顺序进行修改:add_func()
mul_func()
counter
int counter = 0;
int mul_func(int x) {
counter *= x;
x = counter;
return x;
}
int add_func(int x) {
counter += x;
x = counter;
return x;
}
int main(void) {
int sum = add_func(3) + mul_func(2);
}
如果是这样,如果我向它们添加互斥锁是否有帮助?
int counter = 0;
pthread_mutex_d mtx;
int mul_func(int x) {
pthread_mutex_lock(&mtx);
counter *= x;
x = counter;
pthread_mutex_unlock(&mtx);
return x;
}
int add_func(int x) {
pthread_mutex_lock(&mtx);
counter += x;
x = counter;
pthread_mutex_unlock(&mtx);
return x;
}
int main(void) {
pthread_mutex_init(&mtx);
int sum = add_func(3) + mul_func(2);
pthread_mutex_destroy(&mtx);
}
虽然结果可能是不确定的(就像许多多线程过程一样),但即使没有数据争用,“未排序”的性质是否仍然会导致 UB?
答:
这确实取决于编译器来决定它想要调用函数的顺序。它会在添加之前调用两者,但这就是你所知道的。
你的互斥锁不做任何事情;这些是用于同步线程的,而不是强制编译按照您想要的顺序计算内容。你的代码不是并行地做事,它只是以未指定的顺序做事。
强制执行排序的正确方法是
const int left=add_func(3);
const int right=mul_func(2);
int sum=left+right;
这样,编译器别无选择,只能按照您提供的顺序调用函数。
更新:因此,正如其中一条评论中所建议的那样,让我稍微强调一下措辞。根据语言定义,您的代码不是“未定义的行为”;实际上,您得到了两种可能的结果之一,这些结果定义得非常明确:如果编译器决定先调用add_func,然后再调用mul_func则会出现一个结果,而如果编译器更喜欢另一个调用顺序,则会出现另一个结果。
调用这两个函数的顺序是“未指定”的,但它们总是一个接一个地调用,从不并行调用。编译器或 CPU 可能添加的任何引入某种程度并行性的优化都不会改变这一点;您将始终获得两种可能的结果之一。
从另一个角度想一想:如果编译器(或CPU,或上帝)会添加一些随机并行性,这需要你采取某些预防措施,那么对程序行为的这种意外变化会破坏每个软件。 优化总是使用术语,比如不改变“可观察的行为”,这是有原因的。
是的,C++允许某些优化来改变“可观察的行为”,但这在语言规范中明确规定,而不是编译器(或CPU)一时兴起做的事情。
评论
int a = ++i + ++i
3
2
add_func(3)
mul_func(2)
+
i++ + ++i
评论
++i + ++i
i
i + ++i
++i + i
++i + ++i