C语言编程基础-struct和union

来源:互联网 发布:英制螺丝孔算法 编辑:程序博客网 时间:2024/05/18 10:16

最近的笔试题中做到了这样一个题目:

struct S{    char c;    int a;    struct S *p;    union    {        short b;        unsigned int d;    };};该结构体编译到32位系统下的大小是()字节

下面就对结构体struct和共用体union大小一个简单的总结。
环境:Intel Core i5-4590,OS-64bit,CodeBlocks 16.01-32bit


1. struct

计算结构体的大小就是要看结构体是如何对齐的,结构体的对齐也可以说是内存对齐,为什么要进行内存对齐呢,主要是为了方便CPU寻址。

内存对齐关键有以下5点:

(1) 内存对齐与编译器设置有关,所以首先要搞清楚编译器这个默认值是多少;(2) 如果不想按照编译器默认对齐的话,可以通过伪指令#pragma pack(n)指定按照n字节对齐;(3) 结构体变量对齐遵循最小化长度规则。(4) 结构体的大小确定:对齐后的长度必须是成员中最大的对齐参数的整数倍,最大对齐参数由(3)得到;(5) 如果结构体A里面还有结构体B,则结构体B的对齐方式选取它里面最长的成员的对齐方式。

注:
最小化长度规则:每个结构体变量的对齐,如果对齐参数n大于该变量所占字节数m,则按照m对齐,也就是内存偏移后的地址是m的倍数,否则按照n对齐,即内存偏移后的地址是n的倍数。

所以结构体的对齐有这样的三步:
首先确定当前程序的对齐参数;接着计算每个结构体变量的大小和偏移量;最后计算结构体总大小。



默认对齐方式 + 指定对齐方式

(1) 默认对齐方式 - 自然对齐

在默认对齐方式下,结构体成员的内存分配满足下面三个条件

(1) 结构体的首地址就是结构体第一个成员的地址(2) 结构体每个成员地址相对于结构体首地址的“偏移量”是该成员大小的整数倍,如果不是则编译器会在成员之间添加填充字节;(3) 结构体“总的大小”要是其成员中最大size的整数倍,如果不是编译器会在其末尾添加填充字节。
struct s1{    char a;    int b;    double c;    char d;};struct s2{    char a;    int b;    double c;};

输出结果:
s1结构体大小为24

a的偏移量为0b的偏移量为4(按照默认对齐方式(2),编译器在成员a后边填充了3字节)c的偏移量为8d的偏移量为16总长为24(按照默认对齐方式(3),最大成员c占用8字节,在结构体后边添加7字节)

s2结构体的大小为16


(2) 指定对齐方式

对于指定的对齐方式,其成员的地址偏移以及结构的总的大小按指定方式对齐

(1) 结构体第一个成员的地址和结构体的首地址相同(2) 结构体每个成员的"地址偏移"需要满足最小化长度原则    如果n大于结构体成员中最大成员的大小,则n不起作用,仍然按照默认方式对齐(3) 结构体总的大小需要是n的整数倍,如果不是需要在结构体的末尾进行填充

可以通过下面的方法改变缺省的对齐方式

使用伪指令#pragma pack (n),编译器将按照n个字节对齐;使用伪指令#pragma pack (),取消自定义字节对齐方式。
#pragma pack (n)struct aligntype{    char a;    int b;    char c;};#pragma pack ()

当n为4、8、16时,其对齐方式均一样,sizeof(aligntype)的结果都等于12。而当n为2时,其发挥了作用,使得sizeof(aligntype)的结果为8。

现仍以默认对齐方式中的结构体为例

#pragma pack(4)struct s1{    char a;    int b;    double c;    char d;};struct s2{    char a;    int b;    double c;};#pragma pack()

输出结果:
s1结构体大小为20

a的偏移量为0b的偏移量为4(指定对齐方式,编译器在成员a后边填充了3字节)c的偏移量为8d的偏移量为16总长为24(指定对齐方式,n=4小于最大成员c长度起作用,在结构体后边添加3字节)

s2结构体的大小为16

#pragma pack (2)    /*指定按2字节对齐*/struct S{    char b;    int a;    short c;};#pragma pack ()     /*取消指定对齐,恢复缺省对齐*/

结构体地址/大小分析:
第一个变量b的自身对齐值为1,指定对齐值为2,假设S从0x0000开始,那么b存放在0x0000
第二个变量,自身对齐值为4,指定对齐值为2,所以有效对齐值为2,所以顺序存放在0x0002、0x0003、0x0004、0x0005四个连续字节中
第三个变量c的自身对齐值为2,所以有效对齐值为2,顺序存放在0x0006、0x0007中
所以从0x0000到0x00007共八字节存放的是S的变量
又S的自身对齐值为4,所以S的有效对齐值为2(最小化长度规则)
又8%2=0,S只占用0x0000到0x0007的八个字节,所以sizeof(struct S) = 8.

注:
(1) 在使用#pragma pack(n)设定对齐方式一定要是2的整数幂,也就是(1,2,4,8,16,…),不然不起作用的,仍然按照默认方式对齐。
(2) 当结构体中有其他的结构体作为成员时,计算最大成员是不能把结构体成员作为一个整体来计算,要看其每个成员的大小,使用结构体中最长成员的对齐方式。这个将在下面结构体嵌套的例子中进行说明

  • 结构体嵌套
struct stu1{    short i;   struct  {        char c;        int j;  }ss;  int k;};

结构体stu1的大小为16
结构体成员ss.c的偏移量应该是4,而不是 2。

#pragma pack(push)#pragma pack(8)struct s1{    int a;    char b;    int c[20];    long l;};struct s2{    char a1;    char a2;    struct s1 t1;    double b1;};#pragma pack(pop)

结构体s1的大小为92

a的偏移量为0b的偏移量为4c的偏移量为8(8大于int变量大小,以默认方式对齐;区分偏移量和成员大小)l的偏移量为88

结构体s2的大小为104

a1的偏移量为0a2的偏移量为1结构体t1的偏移量为4(t1里面最长成员的“对齐方式”)b1的偏移量为96


2. union

struct中的每个域在内存中都独立分配空间
union只分配最大域的空间,所有域共享这个空间

#include <stdio.h>struct A{    char a;    int b;    double c;};union B{    char a;    int b;    double c;};int main(){    printf("%d\n", sizeof(struct A));    printf("%d\n", sizeof(union B));    return 0;}

输出结果:
结构体A的大小为16
结构体B的大小为8

对于stuct对齐

(1) 先算对齐大小:对齐的大小也是取决于struct成员中字节对齐最大的那个
(2) 再算成员空间:然后根据每个成员的对齐大小对齐每个成员算出分配的空间
(3) 最后算出实际分配的空间,分配struct的实际大小不小于成员空间总和且满足是对齐大小的整数倍

对于union对齐

(1) 先算对齐大小:对齐的大小是取决于union成员中字节对齐最大的那个
(2) 再算实际分配的空间:分配给union的实际大小不能小于最大成员的大小,且要满足是对齐大小的整数倍。

  • 默认对齐
union U1{    char a[9];    int b;};

成员a是char数组,对齐大小为1字节,成员b是int,对齐大小为4字节,所以U1对齐大小为4字节;
分配给U1的实际大小既要是4字节的整数倍,又要不小于最大成员a的大小,即位4的整数倍又要大于9,所以实际分配的空间为12字节。
若将int改为double,则对齐为8,大小为16。

union U2{    U1 a;    double b;};

对比char,int和double,对齐是double的大小,为8,大小应当为是16。

  • 指定对齐
#pragma pack(4)     // pack(0)会采用默认的字节4union U3{    char a[9];    double b;};

基本元素b的对齐由原来的8变为4,而a的对齐仍然是1。U3的对齐由8变为4,大小为12。

所以文章开头题目的答案为:16字节
结构体指针大小在32位系统下为4字节,64位系统下为8字节。



Acknowledgements:
http://www.cnblogs.com/wangguchangqing/p/4853438.html
http://www.cppblog.com/iuranus/archive/2009/01/06/71388.html
http://blog.csdn.net/yuucyf/article/details/7872502
http://blog.csdn.net/csw_100/article/details/5495309
http://www.cnblogs.com/Daniel-G/archive/2012/12/02/2798520.html
http://blog.csdn.net/talentluke/article/details/6108557

2017.04.09
本人博客会根据个人知识升级情况进行补充修改

0 0
原创粉丝点击