C语言位域结构体运算

    今天遇到一个关于C语言位域运算的问题,自己写代码试了一下,在Linux的gcc下编译运行了一下,大概了解了C语言位域运算在gcc下的编译运行情况。
    位域结构体是C语言里为了节约空间而设置的一种特殊的结构体,它的定义类似于普通结构体,只不过,它的每一个成员并不占有一个完整的普通类型结构,比如char,int,short等,而是占有上述结构体中的几bit宽度,定义如下所示:

typedef struct bithead {
    unsigned char a:5;
    unsigned char b:3;
}bithead_t;

    在上面的定义中,成员a占有char中5bit,b占有char中3bit。
    位域定义不能跨“结构”,这个“结构”指的是成员的类型结构,比如上面的bithead结构体里,因为成员是char类型,所以如果b定义的位为5,则需要从下一个char开始,最典型的如下所示:

typedef struct bithead {
    unsigned char a:5;
    unsigned char b:5;
    unsigned char c:5;

}bithead_t;

    上面的结构,因为如果b的前3位跟a放在一个char里的话,后面的2位跟c放在一个char里的话,这个结构体的size应该是2,但运行之后发现长度为3,因此,其实b和c都是放在一个新的char结构里的,因为成员不能“跨”结构。
    但是,另一种情况下,是可以“跨”的,如下所示:

typedef struct bithead {
    unsigned char a:5;
    unsigned int  b:5;
    unsigned char c:5;

}bithead_t;

    大家本能地想,这个结构长度应该是6,而实际上却是4,为什么呢,我理解应该是这样的,就是这个结构体里以最大的类型作为基础类型,因为上面有一个成员b是int型的,长度为4,那么,它不允许“跨”4字节,但是可以“跨”1字节,所以,上面的a和下面的c都可以放进一个int型结构里,所以上面的结构总长度仍为4,可能有人会认为,至少上面的char不能放到下面的b的int型里,下面的char应该可以放进上面的int型结构里,但事实确实是上面的char也放到了int型结构里。
    所以网上有人说不能“跨”字节,我觉得这种说法可能不对,应该是不能“跨”最大的结构,再比如下面的结构:

typedef struct bithead {
    unsigned int a:19;
    unsigned int b:15;
    unsigned int c:18;

}bithead_t;

    上面的结构体长度为12,占用了3个int长度,因为下面的b如果放到上面的int中,超过了一个int,所以必须要放在一个新的int结构里,同样,c也需要放在一个新的int结构里,所以一共需要3个int结构。
    再看下面的结构体:

typedef struct bithead {
    unsigned short a:7;
    unsigned short b:3;
    unsigned short c:6;

}bithead_t;

    这个结构体长为2,其实中间的成员b的3位是“跨”了字节的,但是没有关系,因为它们三个成员是在一个short中,所以是可以放进一个short里的。
    总结以上的结论,C语言中的位域结构,是以结构体中的最大成员类型为基本结构对齐的。只要不“跨”最大的结构类型,就是可以放到一个结构里的。
    下面再看一个数据是怎么放进一个位域结构体中的,看下面的程序:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct bithead{
    unsigned short a:5;
    unsigned short b:5;
    unsigned short c:5;
}bithead_t;

void main(int argc, char* argv[])
{
    bithead_t value;
    printf("Sizeof bithead_t:%ld\n", sizeof(value));
    memcpy((char*)&value, "ab", 2);
    printf("a:0x%02x b:0x%02x c:0x%02x\n", value.a, value.b,value.c);
    
    return;
}

输出结果为:
Sizeof bithead_t:2
a:0x01 b:0x13 c:0x18
为什么是0x01,0x13和0x18呢,因为a的ascii码为10进制的97,b的ascii码为98,二进制分别是:
01100001,01100010
因为主机字节序为little-end小端字节序,所以前面的字符“a”是放在低地址,后面的字符"b"是放在高地址。
而在上面的结构体里,从a到c是从低到高位的,所以拷贝的结果是:
01100010 01100001
如下图所示:

【图暂缺,后面补上】

 转换成16进制,就是上面的结果:
a为0x01,b为0x13,c为0x18
因为我们的自然数都是高位在左,低位在右的,所以上面的图中,我们把低位写在右边,而小端字节序是低位写在低地址,高位写在高地址,而结构体中的成员是前面是低地址,后面是高地址,从前到后排的,这样就更容易理解了。

 

 

[转自本人网易博客,原作时间:2017-02-21 20:38:14]

上一篇:poi操作excel(主要为合并单元格和导出excel)


下一篇:JAVA学习笔记 2021.5.29