我想知道在语言中如何实现按位移位运算符"<<"
和">>"
。是原子还是不原子? c是一次移动整个单词还是逐个移动每一位。
是否对编译器,操作系统或计算机体系结构有任何依赖性?
C标准是否定义了如何实现移位运算符?
示例:
让我们说两个线程正在访问数据。其中一个通过移位3位来修改它。这个3位移位原子操作是否也是如此?我应该使用锁来处理这种修改吗?
编辑:它只是一个班次操作员,没有商店指令。数据已经在内存中,因此无需加载操作。 我的处理器:Powerpc MPC8569,e600核心架构。
答案 0 :(得分:5)
C仅保证{C}中引入的_Atomic
类型变量的原子访问。
对于所有其他情况,从来没有任何原子访问保证。您将不得不反汇编为C代码以查看它生成的汇编程序指令数。通常,一个汇编程序指令始终是原子的。
但是你的问题没有那么多意义,因为没有背景。转变的结果会在哪里?你打算把它存放在某个地方吗?那是两个操作:移位和存储。可能也是一个负载。如果你编写的算法本身并不是原子的,那么你如何期望编译器神奇地让它成为原子?
答案 1 :(得分:3)
这取决于您使用的处理器。
如果存在按位移位的指令,就像大多数x86内核和16位和32位微控制器一样,那么它就是原子的。
但是,如果您有一个没有位移指令的8位微控制器,或者您试图将一个较大的位(例如64位或128位)移位,则该指令可能会占用大量代码。
答案 2 :(得分:0)
这取决于你所谈论的标准。
AFAIU,C11中唯一的原子操作(明确定义为原子操作)是与<stdatomic.h>
你可以想象一个具有4位ALU的TeraHertz处理器;即使是简单的int32_t
添加也不会是原子的。
答案 3 :(得分:0)
我写了两个程序
#include<stdio.h>
int main()
{
int i = 5;
return 0;
}
为PowerPC架构代码1生成的汇编代码是
.file "hello.c"
.section ".text"
.align 2
.globl main
.type main, @function
main:
stwu 1,-32(1)
stw 31,28(1)
mr 31,1
li 0,5
stw 0,8(31)
li 0,0
mr 3,0
lwz 11,0(1)
lwz 31,-4(11)
mr 1,11
blr
.size main, .-main
.ident "GCC: (GNU) 4.2.2"
.section .note.GNU-stack,"",@progbits
第二个代码是
#include<stdio.h>
int main()
{
int i = 5;
i = i<<1;
return 0;
}
为PowerPC体系结构代码2生成的汇编代码是
.file "hello.c"
.section ".text"
.align 2
.globl main
.type main, @function
main:
stwu 1,-32(1)
stw 31,28(1)
mr 31,1
li 0,5
stw 0,8(31)
lwz 0,8(31) // extra
slwi 0,0,1 // extra
stw 0,8(31) // extra
li 0,0
mr 3,0
lwz 11,0(1)
lwz 31,-4(11)
mr 1,11
blr
.size main, .-main
.ident "GCC: (GNU) 4.2.2"
.section .note.GNU-stack,"",@progbits
你看到有三个额外的指令,所以操作不是原子的
我也在Intel i7 PC上编译了这个。结果如下:
为第一个代码生成的汇编代码是:
.file "hello.c"
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl $5, -4(%rbp)
movl $0, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (Ubuntu 4.8.4-2ubuntu1~14.04.1) 4.8.4"
.section .note.GNU-stack,"",@progbits
为代码2生成的汇编代码:
.file "hello.c"
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl $5, -4(%rbp)
sall -4(%rbp) // only one extra instruction
movl $0, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (Ubuntu 4.8.4-2ubuntu1~14.04.1) 4.8.4"
.section .note.GNU-stack,"",@progbits
所以,我的理解是,答案取决于我们正在使用的架构。
答案 4 :(得分:-2)
通常它应该是SHLD / SHRD - 双精度转换(386+) https://web.itu.edu.tr/kesgin/mul06/intel/instr/shld_shrd.html
我认为它是原子的,因为它只是一条指令。否则你可以使用atomic或volatile如果c支持它,C ++ 11支持它。