在一次面试中,我被告知,在C语言中使用++运算符(例如i++)是一个原子操作,而使用“i += 1”不是。我认为在线程安全或原子性方面,这些操作完全相同。我是否遗漏了什么,还是它们实际上确实不同?
在一次面试中,我被告知,在C语言中使用++运算符(例如i++)是一个原子操作,而使用“i += 1”不是。我认为在线程安全或原子性方面,这些操作完全相同。我是否遗漏了什么,还是它们实际上确实不同?
这是毫无意义的。具体要看数据类型、架构和编译器(标准通常不会做一般情况下的原子性保证,除非你使用 C11 原子操作)是否支持,一个操作可能是原子的也可能不是,但我认为一般情况下没有充分的理由证明 i++
是原子的而 i += 1
不是。在表达式结果无用的上下文中,它们很可能实际生成相同的代码。
在C语言中,i++;
和i += 1;
这两个语句是等价的。但是两者都不能保证原子性。
特别地,对于大于系统字长的变量(例如32位系统上的64位变量),增量几乎总是非原子的,因为要想原子地增加这样的变量通常需要显式锁定。此外,一些体系结构不支持直接在内存中递增变量。(也就是说,它们需要一个显式的加载/修改/存储序列。)这些架构无法在没有锁定的情况下原子修改任何变量。
* 当作为单独的语句时,而不是表达式
++i
被定义为 i += 1
。 - M.M面试官是错误的。
我使用gcc-4.8.2编译了两个函数,其中包括-O2
和-S
参数。
void increment1(int *a) {
*a += 1;
}
void increment2(int *a) {
(*a)++;
}
两者生成的汇编代码完全相同
increment1:
.LFB0:
.cfi_startproc
addl $1, (%rdi)
ret
.cfi_endproc
.LFE0:
.size increment1, .-increment1
.p2align 4,,15
.globl increment2
.type increment2, @function
increment2:
.LFB1:
.cfi_startproc
addl $1, (%rdi)
ret
.cfi_endproc
但是在更精确的技术术语上下文中,它们都是“原子写入”,这意味着它们不会提供MAD结果。 如果您在32位或更低位CPU环境中使用int64_t变量,则64位修改会导致多次写操作。没有锁,它不能成为“原子写入”。
您可以在gcc环境中使用“__sync_fetch_and_add(&a, 1)”操作进行原子递增。
++i
和i += 1
实现类似(它们相同是因为表达式的结果是增加后的值)。这与i++
不同,因为表达式的结果是增加之前的值。作为完整语句,这三个是等价的。但它们都不能保证是原子操作。 - Jonathan Leffleri += 1
是原子操作吗?,虽然没有提到i++
或者++i
,但是它们有密切的关联,所以不算是重复问题。 - Jonathan Leffler