C++中unsequenced多修改的行为判定及atomic、volatile修饰的有效性问询
C++中unsequenced多修改的行为判定及atomic、volatile修饰的有效性问询
咱们一步步来拆解你提出的这几个问题,结合C++标准和实际行为来分析:
一、原代码是否存在未定义行为(UB)
首先明确标准的核心规定:
C++标准指出,对同一内存位置产生副作用的无序列操作(unsequenced operations),其行为是未定义的,出自[intro.execution§10]
来看你给出的原代码:
int bar() { static int sid; return ++sid; } void foo(int i1, int i2) { std::cout << i1 << std::endl; std::cout << i2 << std::endl; } int main() { foo(bar(), bar()); }
在main函数里,foo(bar(), bar())的两个bar()调用是无序列的——C++标准没有规定它们的执行顺序。每个bar()里的++sid都会对同一个静态变量sid产生写操作的副作用,这两个写操作属于对同一内存位置的无序列副作用,完全符合标准里定义的未定义行为场景,所以这段代码确实存在UB。
二、将sid改为std::atomic<int>能否解决问题?
来看修改后的代码:
int bar() { static std::atomic<int> sid; return ++sid; }
答案是可以解决未定义行为的问题。原因在于:
std::atomic的前缀++操作是原子操作,确保对sid的读-改-写整个过程不会被打断;- 更关键的是,C++标准明确规定,对原子对象的无序列原子操作不会导致未定义行为——即便两个
bar()的执行顺序不确定(可能先执行左边的,也可能先执行右边的),但每个原子操作本身都是合法的,最终sid会被正确递增两次,foo的两个参数只会是1和2或者2和1,所有可能的行为都是标准定义范围内的,不存在UB。
三、将sid改为volatile int能否解决问题?
再看这段修改:
int bar() { static volatile int sid; sid += 1; return sid; }
答案是不能解决问题,仍然存在未定义行为。很多开发者会误解volatile的用途:
volatile的作用仅仅是告诉编译器:不要对这个变量的访问做优化,每次读写都要直接操作内存。它既不提供原子性(读-改-写过程仍然可能被打断),也不改变操作的“无序列”属性;- 两个
bar()调用还是无序列的,对同一个volatile sid的两次sid +=1仍然是对同一内存位置的无序列副作用,完全符合标准中UB的判定条件,所以这段代码依然是未定义行为。
内容来源于stack exchange
相关产品推荐
相关产品推荐

