C语言位域解析&符号位扩展规则

从一个例子说起:

    int main(void){  
        union{
            int i;
            struct{
                char a : 1;
                char b : 1;
                char c : 2;
            }bits;
        }num;
     
        printf("Input an integer for i(0~15): ");
        scanf("%d", &num.i);
        printf("i = %d, cba = %d %d %d
", num.i, num.bits.c, num.bits.b, num.bits.a); 
        return 0;
    }

输入i值为11,则输出为i = 11, cba = -2 -1 -1。   为什么?

1,位域的定义

    在结构体的定义中,指定元素所占用的bit数, 并指定类型。 按照结构体的成员调用方式进行调用。

2,位域的内存对应规则

    一个字节按照从高位到低位 bit7 ~ bit0,对于位域的定义,是从低位bit0 开始算起的(注意不是从高位开始对应)。也就是说,上面例子中的位域,在一个字节中对应的存储是 0000 ccba, a在最低位,然后是b,和占两个bit的c。 c成员中按照bit3高位、bit2低位存储。

3,大小端问题

    对于小端来说,低字节存放在低地址中,int的存储从0x00地址到0x03地址,依次是 00001011 00000000 00000000 00000000。

    联合体从头开始,是对内存中数据的截断和强转, 根据刚才位域的存储结构,cc的截断是10, b和a的截断都是1。

4,为什么打印出来是负数? ---》补码的规则

    在计算机的内存中,所有的数据存储都是按照补码存储的。 对于有符号数来说,正数的补码是正数自身,负数的补码是反码+1。这都没问题。  

    问题的核心还是符号位。计算机里从低精度数向高精度数转换时,比如从char到short, 又比如这里从10两个bit填充为一个char的8个bit, 肯定会在前面扩展一些bit位,从而达到高精度数的长度。那么扩展时,是补0还是补1呢?这里有个原则就是,有符号数扩展符号位,无符号数扩展0。对应到这里也就是1。注意,这里说的是有符号数和无符号数,对于有符号的正数,因为符号位是0,所以也是补零。然而我们在位域的定义中,定义了abc都是有符号的char型。所以在向8位扩展时,因为第一位都是1,所以往前都扩展1,a和b在内存中为11111111, c为11111110,都是补码。按照%d打印出来以后, 就是-1 和 -2。 如果这里定义成 unsigned char,按照定义前面补0,打印结果就会是正数了。
原文地址:https://www.cnblogs.com/Free-Thinker/p/10445238.html