如何在C中实现按位移位运算符。它是原子的吗?

时间:2016-06-23 11:24:48

标签: c

我想知道在语言中如何实现按位移位运算符"<<"">>"。是原子还是不原子? c是一次移动整个单词还是逐个移动每一位。

是否对编译器,操作系统或计算机体系结构有任何依赖性?

C标准是否定义了如何实现移位运算符?

示例:

让我们说两个线程正在访问数据。其中一个通过移位3位来修改它。这个3位移位原子操作是否也是如此?我应该使用锁来处理这种修改吗?

编辑:它只是一个班次操作员,没有商店指令。数据已经在内存中,因此无需加载操作。 我的处理器:Powerpc MPC8569,e600核心架构。

5 个答案:

答案 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支持它。