zoukankan      html  css  js  c++  java
  • 位运算:有符号整数右移和无符号整数右移的区别

    如果我们定义一个符号整数 int a = 0x80000000; 然后执行 a = a >> 1; 那么a将变为0xc0000000;

    我们再定义一个符号整数 unsigned int b = 0x80000000; 然后执行 b = b >> 1; 那么b则将变为0x40000000;

    为什么有这样的差别呢? 先写一小段代码,看看右移的演变过程:

     1 #include <stdio.h>
     2 
     3 int
     4 main(int argc, char *argv[])
     5 {
     6              int a = 0x80000000;
     7     unsigned int b = 0x80000000;
     8 
     9     (void) printf("|%2s| %10s | %10s
    ", "ID", "int", "unsigned int");
    10     (void) printf("|%2d| 0x%08x | 0x%08x
    ", 0, a, b);
    11     for (unsigned int i = 1; i <= 32; i++) {
    12         a = a >> 1;
    13         b = b >> 1;
    14         (void) printf("|%2d| 0x%08x | 0x%08x
    ", i, a, b);
    15     }
    16 
    17     return (b & a);
    18 }

    编译和执行,

    $ gcc -g -Wall -std=gnu99 -o foo foo.c
    
    $ ./foo
    |ID|        int | unsigned int
    | 0| 0x80000000 | 0x80000000
    | 1| 0xc0000000 | 0x40000000
    | 2| 0xe0000000 | 0x20000000
    | 3| 0xf0000000 | 0x10000000
    | 4| 0xf8000000 | 0x08000000
    | 5| 0xfc000000 | 0x04000000
    | 6| 0xfe000000 | 0x02000000
    | 7| 0xff000000 | 0x01000000
    | 8| 0xff800000 | 0x00800000
    | 9| 0xffc00000 | 0x00400000
    |10| 0xffe00000 | 0x00200000
    |11| 0xfff00000 | 0x00100000
    |12| 0xfff80000 | 0x00080000
    |13| 0xfffc0000 | 0x00040000
    |14| 0xfffe0000 | 0x00020000
    |15| 0xffff0000 | 0x00010000
    |16| 0xffff8000 | 0x00008000
    |17| 0xffffc000 | 0x00004000
    |18| 0xffffe000 | 0x00002000
    |19| 0xfffff000 | 0x00001000
    |20| 0xfffff800 | 0x00000800
    |21| 0xfffffc00 | 0x00000400
    |22| 0xfffffe00 | 0x00000200
    |23| 0xffffff00 | 0x00000100
    |24| 0xffffff80 | 0x00000080
    |25| 0xffffffc0 | 0x00000040
    |26| 0xffffffe0 | 0x00000020
    |27| 0xfffffff0 | 0x00000010
    |28| 0xfffffff8 | 0x00000008
    |29| 0xfffffffc | 0x00000004
    |30| 0xfffffffe | 0x00000002
    |31| 0xffffffff | 0x00000001
    |32| 0xffffffff | 0x00000000

    从上面输出的结果中,我们不难看出:

    • 对于符号整数,每一次右移操作,高位补充的是1
    • 对于符号整数,每一次右移操作,高位补充的则是0

    规律找到了,下面“透过现象看本质”,反汇编看看其根本原因:

     1 (gdb) set disassembly-flavor intel
     2 (gdb) disas /m main
     3 Dump of assembler code for function main:
     4 5    {
     5    0x0804841d <+0>:    push   ebp
     6    0x0804841e <+1>:    mov    ebp,esp
     7    0x08048420 <+3>:    and    esp,0xfffffff0
     8    0x08048423 <+6>:    sub    esp,0x20
     9 
    10 6                 int a = 0x80000000;
    11    0x08048426 <+9>:    mov    DWORD PTR [esp+0x14],0x80000000
    12 
    13 7        unsigned int b = 0x80000000;
    14    0x0804842e <+17>:    mov    DWORD PTR [esp+0x18],0x80000000
    15 ...<snip>...
    16 
    17 11        for (unsigned int i = 1; i <= 32; i++) {
    18    0x0804847e <+97>:    mov    DWORD PTR [esp+0x1c],0x1
    19    0x08048486 <+105>:    jmp    0x80484b9 <main+156>
    20    0x080484b4 <+151>:    add    DWORD PTR [esp+0x1c],0x1
    21    0x080484b9 <+156>:    cmp    DWORD PTR [esp+0x1c],0x20
    22    0x080484be <+161>:    jbe    0x8048488 <main+107>
    23 
    24 12            a = a >> 1;
    25    0x08048488 <+107>:    sar    DWORD PTR [esp+0x14],1
    26 
    27 13            b = b >> 1;
    28    0x0804848c <+111>:    shr    DWORD PTR [esp+0x18],1
    29 
    30 14            (void) printf("|%2d| 0x%08x | 0x%08x
    ", i, a, b);
    31 ...<snip>...
    32 18    }
    33    0x080484c8 <+171>:    leave  
    34    0x080484c9 <+172>:    ret    
    35 
    36 End of assembler dump.
    37 (gdb) 

    注意L24-L28,

    24 12            a = a >> 1;
    25    0x08048488 <+107>:    sar    DWORD PTR [esp+0x14],1
    26 
    27 13            b = b >> 1;
    28    0x0804848c <+111>:    shr    DWORD PTR [esp+0x18],1

    原来如此,对于符号整数,右移采用的是sar指令; 而对于符号整数,右移则采用的是shr指令。

    sar : 算术右移 Arithmetic Right Shift | sal : 算术左移 arithmetic left shift
    shr : 逻辑右移 logic Right SHift      | shl : 逻辑左移 logic left shift

    推荐阅读: Arithmetic shift and Logical shift

    总结:

    • 对于左移,无论是算术左移(sal)还是逻辑左移(shl),低位补充的都是0
    • 对于右移,算术右移(sar)高位补1,逻辑右移(shr)高位补0
    • 算术移位应用于有符号数,逻辑移位则应用于无符号数。 (AsLu)
  • 相关阅读:
    Spring MVC:框架及其组件介绍
    Goods:生成订单
    Goods:我的订单查询分页
    LeetCode:Kth Largest Element in an Array(need update)
    LeetCode:Swap Nodes in Pairs
    Goods:购物车条目加减数量实现
    LeetCode:Sum Root to Leaf Numbers
    Goods:购物车模块之全选按钮与条目之复选按钮的click事件
    Goods:查询某个用户的购物车条目以及添加购物车条目
    洛谷2387 NOI2014魔法森林(LCT维护最小生成树)
  • 原文地址:https://www.cnblogs.com/idorax/p/6305476.html
Copyright © 2011-2022 走看看