在 C 中,如果您的编译器很糟糕,它们只会有所不同(对于整数类型)(或者您像 MSVC 答案显示的那样禁用了优化)。
也许以这种方式告诉你的人试图使用 C 语法描述像 sub reg,reg 这样的 asm 指令,不是谈论这样的语句将如何实际上编译现代优化编译器?在这种情况下,我不会对大多数 x86 CPU 说“非常不同”;大多数 do 特殊情况 sub same,same 作为归零习惯用法,如 xor same,same。 What is the best way to set a register to zero in x86 assembly: xor, mov or and?
这使得 asm sub reg,reg 类似于 mov reg,0,但代码大小更好。 (但是,是的,英特尔 P6 系列上的部分寄存器重命名有一些独特的好处,您只能从归零习语中获得,而不是 mov)。
如果您的编译器尝试在弱排序的 ISA(如 ARM 或PowerPC,其中n=0 打破了对旧值的依赖关系,但n = n-n; 仍然“带有依赖关系”,因此像array[n] 这样的负载将在n = atomic_load_explicit(&shared_var, memory_order_consume) 之后进行依赖排序。更多详情请见Memory order consume usage in C11
实际上,编译器放弃了尝试正确进行依赖跟踪并将consume 加载提升到acquire。 http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0371r1.html 和 When should you not use [[carries_dependency]]?
但是在弱序 ISA 的 asm 中,需要sub dst, same, same 仍然携带对输入寄存器的依赖,就像在 C 中一样。(大多数弱序 ISA 是具有固定 -宽度指令,因此避免立即操作数不会使机器代码更小。因此,即使在像 ARM 这样没有架构零寄存器的 ISA 上,也没有像 sub r1, r1, r1 这样的较短归零习惯用法的历史用途。mov r1, #0 是与任何其他方式一样大小和至少一样高效。在 MIPS 上,您只需 move $v0, $zero)
所以是的,对于那些非 x86 ISA,它们在 asm 中非常不同。 n=0 避免了对变量(寄存器)旧值的任何错误依赖,而 n=n-n 在 n 的旧值准备好之前无法执行。
只有 x86 特殊情况 sub same,same 和 xor same,same 作为像 mov eax, imm32 这样的破坏依赖的归零习惯用法,因为 mov eax, 0 是 5 个字节,但 xor eax,eax 只有 2 个字节。所以在乱序执行 CPU 之前使用这种窥视孔优化的历史由来已久,而这种 CPU 需要有效地运行现有代码。 What is the best way to set a register to zero in x86 assembly: xor, mov or and? 解释详情。
除非你用 x86 asm 手写,否则像普通人一样写 0 而不是 n-n 或 n^n,并让编译器使用异或归零作为窥视孔优化。
其他 ISA 的 Asm 可能有其他窥视孔,例如另一个答案提到了m68k。但同样,如果您使用 C 语言编写,这是编译器的工作。当你的意思是0时,写0。尝试“手持”编译器使用 asm 窥视孔不太可能在禁用优化的情况下工作,并且在启用优化的情况下,如果需要,编译器将有效地将寄存器归零。