美文网首页
重温C语言(9)之位运算

重温C语言(9)之位运算

作者: 鸡蛋绝缘体 | 来源:发表于2020-04-03 23:18 被阅读0次

    https://mp.weixin.qq.com/s/Srz_K3YVLWwH7LBQeHPVZg

    [TOC]

    1 运算符

    ~,&,|,^,<<,>>,&=,|=,^=,>>=,<<=

    2 二进制

    以2为基底表示的数字为二进制。

    二进制1111可表示为:

    1*2^3 + 1*2^2 + 1*2^1 + 1*2^0
    

    字节表示储存系统字符集所需的大小,1字节包含8位。
    二进制无符号整数的范围是 0 ~ 255;
    有符号整数的范围是 -127 ~ 127,因为八位的第一位储存符号(1负 0正),只剩下7位表示数字本身。也会产生 +0 和 -0 两个0.

    原码,反码, 补码

    计算机中存储的数据都是以二进制码存储的,程序对正数和负数都要进行计算,因此增加了符号位来区分正数和负数。计算机以二进制补码来表示有符号位。

    计算机中的运算器只有加法运算器,所以计算机只能做加法,减一个数的运算也是通过加法实现相当于加上一个这个数的相反数。

    • 正数:原码=反码=补码
    • 负数:原码(符号位是1)=反码(除符号位,其他位取反)=补码(反码+1)
    • 0:原码=反码=补码=0

    以8位二进制为例:
    一个二进制和按位取反的结果相加,和必定是 11111111-1, 例如十进制2 00000010 反码是 11111101 它两相加(00000010 + 11111101 = 11111111)结果是 -1, 即 数+相反数=-1 得出 数+相反数+1=0 因而补码是 相反数+1 的结果。

    2 - 2 举例: 即 2 + (-2),2在程序中的补码是00000010,-2 在程序中的原码是 10000010 反码是 11111101 补码(计算机中存储的数值)是 11111110 可以得出 00000010 + 11111110 = 00000000

    6 - 8 举例: 即 6 + (-8),6在程序中的补码是00000110,-8 在程序中的原码是 10001000 反码是 11110111 补码(计算机中存储的数值)是 11111000 可以得出 00000110 + 11111000 = 1111111011111110-2 的补码。

    以此类推,总的来说补码让计算机可以进行减法运算。

    3 按位逻辑运算符

    (1)二进制反码或按位取反: ~

    -(10011010) 的结果是 (01100101), 取反就是0变1,1变0.

        unsigned char a = 2;  // 00000010
        a = ~a;
        printf("%d", a);   // 11111101 即输出 253
    
        char a = 2;  // 00000010
        printf("%d", ~a);   
        //输出 -3,因为~a取反是11111101第一个是符号位,计算机判断负数,取补码 10000011 即 -3
    

    (2)按位与: &

    (10010011)&(00111101) 结果是 00010001, 按位与就是两个对比位都是1才是1,否则是0.

        unsigned char a = 2;   // 00000010
        unsigned char b = 1;    // 00000001
        printf("%d", a & b);    // 00000000
    

    使用掩码,就是按位与运算的过程。

    (3)按位或:|

    (10010011)|(00111101) 结果是 10111111, 按位与就是两个对比位只要有一位是1结果就是是1,否则是0.

    (4)按位异或: ^

    (10010011)^(00111101) 结果是 10101110, 按位与就是两个对比位只要不同结果就是是1,否则是0.

    4 移位运算符

    (1)左移:<<

    (10010011) << 2 的结果是 01001100, 左移运算即把每一位向左移动指定位数,移出左端的值丢失,右端空出的位置补0.
    一个数左移2位相当于该数乘以2的2次幂的结果。

    (2)右移:>>

    对无符号类型 (10010011) >> 2 的结果是 00100100;
    对有符号位 (10010011) >> 2 的结果是 0010010011100100, 根据系统的不同补充位可能是0也可能是符号位的副本;
    右移运算即把每一位向右移动指定位数,移出右端的值丢失,对有符号值左端空出的位置根据系统的不同补0或符号位的副本,对无符号值用0填充。

    对于非负数,一个数右移2位相当于该数除以2的2次幂的结果。

    编程:十进制转换为二进制

    #include "limits.h"
    
    // 这个方法将整数转换为二进制字符串
    char * itobs(int n, char * ps){
        int i;
        const static int size = CHAR_BIT * sizeof(int);
        for (i = size - 1; i >= 0; i--, n >>= 1) {
            ps[i] = (01 & n) + '0';
        }
        ps[size] = '\0';
        return ps;
    }
    
        printf("sizeof(int) = %ld\n", sizeof(int));
        char bit_str[CHAR_BIT * sizeof(int) + 1];
        int number = 11;
        itobs(number, bit_str);
        printf("%d 的二进制是:\n", number);
        for (int i = 0; bit_str[i]; ) {
            putchar(bit_str[i++]);
        }
    

    输出:

    sizeof(int) = 4
    11 的二进制是:
    00000000000000000000000000001011
    
    -11 的二进制是:
    11111111111111111111111111110101
    

    5 位字段

    struct {
        unsigned int a: 1;
        unsigned int b: 1;
        unsigned int c: 1;
        unsigned int d: 1;
    } prnt;
    

    prnt 包含 4 个 1位的字段,a,b,c,d是位字段,它们都确定了字段的宽度是1, 因为只有1位,所以只能赋值 0或1。
    位字段是一个signedint或unsignedint类型变量中的一组相邻的位。可以通过位字段来规定字段的大小。

    6 对齐特性

    _Alignof运算符给出一个类型的对齐要求,在关键字_Alignof后面的圆括号中写上类型名即可:

    size_t f_align = _Alignof(float);
    

    如果 f_align 的值是4,就代表float 类型对象的对其要求是4,即存储float类型值需要相邻地址的字节数是4.

    使用_Alignas说明符指定一个变量或类型的对齐值。但是,不应该要求该值小于基本对齐值。

    _Alignas(float) char c1;
    _Alignas(8) char c2;
    

    C11在stdlib.h库还添加了一个新的内存分配函数,用于对齐动态分配的内存。该函数的原型如下:

    void * aligned_alloc(size_t alignment, size_t size);
    

    第1个参数代表指定的对齐,第2个参数是所需的字节数,其值应是第1个参数的倍数。与其他内存分配函数一样,要使用free()函数释放之前分配的内存。

    相关文章

      网友评论

          本文标题:重温C语言(9)之位运算

          本文链接:https://www.haomeiwen.com/subject/tdxrphtx.html