[C语言] 详解结构体字节对齐相关问题

一. 结构体字节对齐

1. 什么是字节对齐

  要了解字节对齐这个概念,先通过下面的代码进行观察:

struct Test
{
	char a;
	int b;
	char c;
};

printf("sizeof(Test)=%d\n", sizeof(Test)); 
// 输出:
// sizeof(Test)=12

  按照结构体中变量的字节大小,我们认为sizeof(Test)=6,但输出结果与我们预想并不一样,所以在结构体中变量所占的字节大小发生了变化,这就叫做字节对齐。

2.为什么字节对齐

  计算机一般读取内存中的数据一般都喜欢以2的整数倍去进行读取,在结构体中数据都存储栈中,在读取数据时,如果结构体中的变量存储字节大小长短不一,计算机在读取数据时不能一次就将数据读取出来。就大大损失了计算机的性能。所以我们扩大每个字节的所占的字节数,来提高计算机读取速度。以空间换取时间。
在这里插入图片描述

3. 如何计算字节对齐

  这里有关于字节对齐的四大概念,计算字节对齐紧扣概念:

 1. 自身类型有一个对齐值(一般按照下方的成员进行对齐)
 2. 自定义类型有一个对齐值(定义的结构体类型,一般按照成员中最大的一个对齐)
 3. 程序有一个指定的对齐值 (#pragma pack(n))
 4. 程序又一个有效对齐值(如果指定对齐值,按照指定值和成员中最小的一个进行对齐)

通过几个例子解释上述概念:

// 概念1 2
struct S1	// 12
{
	char c1;	// 1+3 根据下方成员对齐
	int i;		// 4
	char c2;	// 1+3	自定义类型按照成员中较大的对齐 这三个成员加起来为9 不是int型的整数倍,所以末尾补3
};

// 概念1
struct S2	// 8
{
	char c1;	// 1
	char c2;	// 1+2  根据下方成员对齐
	int i;		// 4
};

// 上同
struct S3	// 16
{
	double a;	// 8
	char c;		// 1 + 3 按照下方成员对齐
	int i;		// 4
};

struct S4		// 32
{
	char c;			// 1+7	按照结构体成员中最大的对齐 S3中最大字节为8 所以按照8对齐
	struct S3 s3;	// 16
	double d;		// 8
};

// 概念3 4:
#pragma pack(4)	// 指定程序按照4字节对齐
struct Test1   // 8
{
	char a;		// 1 + 1	// 虽然指定了对齐值,但是程序又一个有效对齐值,按照内部成员较小值对齐,所以按照short对齐
	short b;	// 2
	int c;		// 4
};

// 概念3 4:
#pragma pack(2)
struct Test2	// 14
{
	char a;		// 1+ 1
	double b;	// 8
	int c;		// 4
};

struct Test3	// 40
{
	short a;		// 2 + 6	按照内部结构体中的最大值对齐 按照double对齐
	struct
	{
		int b;		// 4 + 4
		double c;	// 8
		char d;		// 1 + 7
	};
	long e;			// 4 + 4
};

#pragma pack(2)
struct Test4	// 20
{
	short a;		// 2	有指定对齐字节数 所以要按照较小值对齐所以按照2对齐
	struct
	{
		int b;		// 4
		double c;	// 8
		char d;		// 1 + 1
	};
	long e;			// 4
};

#pragma pack(4)
struct Test5	// c++: 8字节  c: 32字节
{
	int a;				// 4
	struct test		// 这是类型定义 再c++中不占空间 
	{
		double b;		// 8
		char c[10];		// 10 + 2
		int d;			// 4
	};
	long e;			// 4
};

#pragma pack(8)
struct Test6  // 3
{
	char a;		// 1	按照结构体中最小的成员对齐
	char b;		// 1
	char c;		// 1
};

4. 位段

  位段是给整型类型指定所占位数的一种概念。
  位段的特点:1. 不能跨字节存储 2.不能跨类型存储

struct Test1	// 1字节
{
	char a : 2;		// 指定a 占用2位
	char b : 5;		// 指定b 占用5位
	char c : 1;		// 指定c 占用1位
};
// 所占空间8位 刚好一个字节

struct Test2	// 2字节
{
	char a : 2;
	char b : 5;
	// 不能跨字节存储 因为前两个变量已经占用了第一个字节的7位,c不能拆分分别放入两个字节中,所以要开辟一个新的字节进行存储
	char c : 2;
};

struct Test3	// 8字节
{
	// 不同类型 不能一起存储 并且还需要按照结构体字节对齐规则进行对齐
	char a : 1;		// 1 + 3
	int b : 4;		// 4
};

定义了位段的类型,是如何在内存中进行存储的:

// 数据如何在位段中进行存储的
// 存储时根据位数进行存储,如果数据超出位数表示范围则进行截断
struct Test4	// 占用3个字节
{
	char a : 3;
	char b : 4;
	char c : 5;
	char d : 4;
};
int main()
{
	Test4 s = { 0 };
	s.a = 10;
	s.b = 12;
	s.c = 3;
	s.d = 4;
}

上述程序,存储示例:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值