计算结构体大小-结构体内存对齐(详解)

文章详细介绍了C语言中结构体的内存对齐规则,包括第一个成员从偏移量0开始、对齐数的考虑以及结构体大小的计算。提到了内存对齐的原因,如平台兼容性和性能优化,并通过示例展示了如何通过#pragmapack来修改默认对齐数。同时,文章也讨论了位段的使用及其跨平台问题,指出位段虽能节省空间但存在不确定性。
摘要由CSDN通过智能技术生成

前言:内容包括:计算结构体大小的详解,修改默认对齐数,位段

结构体的对齐规则:

所有的第一个成员变量都是从偏移量为0的位置开始存放的,直接存 

 实例:

1 结构体大小:12

struct S1
{
    //默认对齐数:8
	char c1;//对齐数:1  因为1(c1的大小,1字节)<8(默认对齐数)
	int i;//对齐数:4 因为4(i的大小,4字节)<8(默认对齐数)
	char c2;//对齐数:1
//最大对齐数:4
};
int main()
{
	printf("%d\n", sizeof(struct S1));
	return 0;
}

右边的数字代表偏移量 

 2 结构体大小:8

struct S2
{
    char c1;//对齐数:1
    char c2;//对齐数:1
    int i;//对齐数:4
//最大对齐数:4
};
    printf("%d\n", sizeof(struct S2));

 3 结构体大小:16

struct S3
{
    double d;//对齐数:8
    char c;//对齐数:1
    int i;//对齐数:4
    //最大对齐数:8
};
int main()
{
    printf("%d\n", sizeof(struct S3));
    return 0;
}

 c的对齐数是1,任何数字都是1的倍数,故而c可以存放在8偏移处

i的对齐数是4,而9,10,11都不是4的倍数,故而i要在12偏移处才开始存放

4 结构体大小:32

struct S3
{
    double d;//对齐数:8
    char c;//对齐数:1
    int i;//对齐数:4
    //最大对齐数:8
};

struct S4
{
    char c1;//对齐数:1
    struct S3 s3;//对齐数:8(因为结构体s3的所有成员中的最大对齐数是8)大小16字节,实例3已经算过
    double d;//对齐数:8
    //最大对齐数:8
};

int main()
{
    printf("%d\n", sizeof(struct S4));
    return 0;
}

 为什么存在内存对齐:

1. 平台原因(移植原因):
不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特
定类型的数据,否则抛出硬件异常
2. 性能原因:
数据结构(尤其是栈)应该尽可能地在自然边界上对齐。
原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访

访问未对齐的内存:

访问对齐的内存:

 总体来说:

结构体的内存对齐是拿空间换取时间的做法

设计结构体的时候,既要满足对齐,又要节省空间,如何做到:
让占用空间小的成员尽量集中在一起
 

//例如:
struct S1
{
    char c1;
    int i;
    char c2;
};//结构体大小:12
struct S2
{
    char c1;
    char c2;
    int i;
};//结构体大小:8

int main()
{
    printf("%d\n", sizeof(struct S1));
    printf("%d\n", sizeof(struct S2));
    return 0;
}

 显然第二个结构体大小更加节省空间

修改默认对齐数:

#pragma pack(数字)修改默认对齐数
#pragma pack() 还原默认对齐数
#pragma pack(8)//设置默认对齐数为8
struct S1
{
	char c1;
	int i;
	char c2;
};//结构体大小:12
#pragma pack()//取消设置的默认对齐数,还原为默认

#pragma pack(1)//设置默认对齐数为1
struct S2
{
	char c1;
	int i;
	char c2;
};//结构体大小:6
#pragma pack()//取消设置的默认对齐数,还原为默认

int main()
{
	printf("%d\n", sizeof(struct S1));
	printf("%d\n", sizeof(struct S2));
		return 0;
}

结构在对齐方式不合适的时候,我们可以自己更改默认对齐数

位段:

位段的声明和结构是类似的
位段的成员名后边有一个冒号和一个数字
比如:

struct A
{
    int _a:2;
    int _b:5;
    int _c:10;
    int _d:30;
};

数字代表:比特位

1. 位段的成员可以是 int unsigned int signed int 或者是 char (属于整形家族)类型
2. 位段的空间上是按照需要以4个字节( int )或者1个字节( char )的方式来开辟的。
3. 位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使用位段

内存分配: 

struct S
{
	char a : 3;
	char b : 4;
	char c : 5;
	char d : 4;
};
struct S s = { 0 };
s.a = 10;
s.b = 12;
s.c = 3;
s.d = 4;

 可以看到在vs中前3个字节的内容是:62 03 04

位段的跨平台问题:

1. int 位段被当成有符号数还是无符号数是不确定的。
2. 位段中最大位的数目不能确定。(16位机器最大16,32位机器最大32,写成27,在16位机
器会出问题。
3. 位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。
4. 当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是
舍弃剩余的位还是利用,这是不确定的
跟结构相比,位段可以达到同样的效果,可以很好的节省空间,但是有跨平台的问题存在
 


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值