(必看)C结构体与C结构体之位域(位段)的sizeof问题.

来源:互联网 发布:苹果签名软件 编辑:程序博客网 时间:2024/05/01 12:37

C结构体总结(自己总结)

1.结构体变量的首地址应该是结构体大小的整数倍(当结构体大小大于处理器位数(现在一般是64位即8字节)时,应该是处理器位数(字节数)的整数倍)

2.结构体成员相对于结构体首地址的偏移量应该是该成员大小的整数倍。(当成员大小大于处理器位数时,偏移量应该为处理器位数的整数倍)(如有需要编译器会在成员之间加上填充字节 

3.结构体总大小为结构体最大成员长度的整数倍(如果结构体最大成员的大小大于处理器位数,侧结构体的总大小为处理器位数的整数倍)

struct A

{

int b;      //0 相对于首地址的偏移量

double c;   //8

char a;     //16

};             //sizeof(struct A)=24

struct B       //最大处理器为64

struct A a;  // 0

char b;     //24

};            //sizeof(struct B)=32

C结构体之位域(位段)

http://www.itmian4.com/forum.php?mod=viewthread&tid=2863&extra=page%3D1

有些信息在存储时,并不需要占用一个完整的字节, 而只需占几个或一个二进制位。例如在存放一个开关量时,只有0和1 两种状态, 用一位二进位即可。为了节省存储空间,并使处理简便,C语言又提供了一种数据结构,称为“位域”或“位段”。所谓“位域”是把一个字节中的二进位划分为几个不同的区域, 并说明每个区域的位数。每个域有一个域名,允许在程序中按域名进行操作。 这样就可以把几个不同的对象用一个字节的二进制位域来表示。
一、位域的定义和位域变量的说明位域定义与结构定义相仿,其形式为:

struct 位域结构名 { 位域列表};

其中位域列表的形式为:

类型说明符 位域名:位域长度

位域变量的说明与结构变量说明的方式相同。 可采用先定义后说明,同时定义说明或者直接说明这三种方式。例如:

struct bs{  int a:8;  int b:2;  int c:6;}data; 

说明data为bs变量,共占两个字节。其中位域a占8位,位域b占2位,位域c占6位。对于位域的定义尚有以下几点说明:


1. 一个位域必须存储在同一个字节中,不能跨两个字节。如一个字节所剩空间不够存放另一位域时,应从下一单元起存放该位域。也可以有意使某位域从下一单元开始。例如:

struct bs{    unsigned a:4    unsigned b:5 /*从下一单元开始存放*/    unsigned c:4}

2. 由于位域不允许跨两个字节,因此位域的长度不能大于一个字节的长度。

3. 位域可以无位域名,这时它只用来作填充或调整位置。无名的位域是不能使用的。例如:

struct k

{

    int a:1

    int :2 /*无位域名,该2位不能使用*/

    int b:3

    int c:2

};

复制代码

二、位域的使用

下面例子是参加一个公司(白领科技-青岛)的笔试遇到的,当时做错了,为了怕忘了,赶紧写下来。

#include <iostream>

2 #include <memory.h>

10 3 using namespace std;

11 4 struct A

12 5 {

13 6     int a:5;

14 7     int b:3;

15 8 };

16 9 int main(void)

17 10 {

18 11     char str[100] = "0134324324afsadfsdlfjlsdjfl";

19 12         struct A d;

20 13     memcpy(&d, str, sizeof(A));

21 14     cout << d.a << endl;

22 15     cout << d.b << endl;

23 16     return 0;

24 17 }

复制代码

在32位x86机器上输出:

25 $ ./langxun.exe

26 -16

27 1

复制代码


解析:在默认情况下,为了方便对结构体内元素的访问和管理,当结构体内的元素长度都小于处理器的位数的时候,便以结构体里面最长的元素为对其单位,即结构体的长度一定是最长的数据元素的整数倍;如果有结构体内存长度大于处理器位数的元素,那么就以处理器的位数为对齐单元。由于是32位处理器,而且结构体中a和b元素类型均为int(也是4个字节),所以结构体的A占用内存为4个字节。

上例程序中定义了位域结构A,两个个位域为a(占用5位),b(占用3位),所以a和b总共占用了结构A一个字节(低位的一个字节)。

当程序运行到14行时,d内存分配情况:

d.a内存中二进制表示为10000,由于d.a为有符号的整型变量,输出时要对符号位进行扩展,所以结果为-16(二进制为11111111111111111111111111110000)

d.b内存中二进制表示为001,由于d.b为有符号的整型变量,输出时要对符号位进行扩展,所以结果为1(二进制为00000000000000000000000000000001)

三、位域的对齐

  如果结构体中含有位域(bit-field),那么VC中准则是:

  1) 如果相邻位域字段的类型相同,且其位宽之和小于类型的sizeof大小,则后面的字段将紧邻前一个字段存储,直到不能容纳为止;

  2) 如果相邻位域字段的类型相同,但其位宽之和大于类型的sizeof大小,则后面的字段将从新的存储单元开始,其偏移量为其类型大小的整数倍;

  3) 如果相邻的位域字段的类型不同,则各编译器的具体实现有差异,VC6采取不压缩方式(不同位域字段存放在不同的位域类型字节中),Dev-C++和GCC都采取压缩方式;

  系统会先为结构体成员按照对齐方式分配空间和填塞(padding),然后对变量进行位域操作。

以上为别人资料:自己总结如下:

上面说的由于位域不允许跨两个字节,因此位域的长度不能大于一个字节的长度这是错误的 位域的长度只要不大于该位域类型的长度即可.(比喻 int a:17 这是合法的)

关于位于的sizeof():

1) 如果相邻位域字段的类型相同,且其位宽之和小于类型的sizeof大小,则后面的字段将紧邻前一个字段存储,直到不能容纳为止;

2) 如果相邻位域字段的类型相同,但其位宽之和大于类型的sizeof大小,则后面的字段将从新的存储单元开始,其偏移量为其类型大小的整数倍;

3) 如果相邻的位域字段的类型不同,则各编译器的具体实现有差异,VC6采取不压缩方式(不同位域字段存放在不同的位域类型字节中),Dev-C++和GCC都采取压缩方式;

关于3在vs2008下调试结果,采用不压缩方式,其偏移量为其类型大小的整数倍自己在vs2008下测试

C结构体总结的3条对于c结构体位域也适用

struct bs 

{

char a:2;  //偏移量为0  相对于首地址的偏移量

    int b:15;  //偏移量为4

int c:16;  //可以与面合并

};           //sizeof(struct bs)=8

struct bs 

{

char a:2;  //偏移量为0

    int b:15;  //偏移量为4

int c:18;  //偏移量为不能合并

};           //sizeof(struct bs)=12

原创粉丝点击