C语言-结构体对齐

本文围绕C语言结构体对齐展开,介绍了结构体对齐编译修饰,如#pragma pack()的不同用法及各对齐方式特点,还阐述了结构体大小的规则,包括结构体大小是成员最大变量大小的整数倍,成员变量地址需满足自身大小的整数倍关系,以提升数据存取效率和合理利用存储空间。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

详细说明参考博客

(1条消息) C语言结构体对齐,超详细,超易懂_haozigegie的博客-优快云博客

(1条消息) #pragma pack详解_OuJiang2021的博客-优快云博客_#pragma pack

以下个人理解总结

出现结构体对齐考虑的根本原因就是:【数据存取执行效率】和【存储空间】的博弈。

1、结构体对齐编译修饰

#pragma pack()    恢复默认对齐方式

#pragma pack(n )   n -1,2,4,8 对齐方式选择 ,n=1表示地址连续分配,无对齐

#pragma pack(show) 显示当前字节对齐方式的字节数,以warning显示。keil-STM32环境pack默认是8.

  • n = 8 表示 1、2、4、8字节的数据都能够一次性快速读写。一般默认是8字节对齐。  
  • n = 4 表示 1、2、4字节数据都能一次性读写。8字节数据可能需要分成两次才能完成一次读写。
  • n = 2 表示 1、2 字节的变量可以一次性读写,4字节变量可能会被分成两次才能读写。8字节可能会更多次。
  • n = 1 为最紧密的存储方式。没有浪费多余的空闲地址。一般在数据打包解析数据流时用到。
#pragma pack(1)
typedef struct
{
	uint8_t a;
	uint16_t b;
	uint32_t c;
}type1;
typedef struct
{
	uint32_t c;
	uint16_t b;
	uint8_t a;
}type2;
 type1 t1;
int  test()
{
	t1.a =0xFF;
	t1.b = 0xFFFF;
	t1.c = 0xFFFFFFFF;
	type2 t2;
	int size = sizeof(t1);
	int size2 = sizeof(t2);
	return size ;
}

 此处存放未对齐32位数据用了 

        STR      r2,[r3,#0x03] 

那就是一条就赋值过去了,CotexM3有这个能力额,和开始想的两次写入有点出入。其他平台可就没这么厉害了,比如51单片机就不可以。

2、结构体大小

上文提到结构体对齐是为了快速访问存储器。

虽然在STM32上测试不对奇也有汇编指令STR快速操作存储器。这里有硬件支持不做特殊说明。但是不影响咱们去理解和接受其原理。

 说明:默认状态下,pack = 8

规则1整个结构体的大小等于其成员单个最大变量大小的整数倍数。比如结构体中变量最大2字节,那么最终结构体大小也是2的倍数。如果最大变量时4,则结构体大小就是4的倍数,以此类推。该特性用于占位结构体末尾未对齐的空间

规则2、结构体成员变量每个变量都会找到自己的位置,它自身大小时固定的,唯独它自省和前面变量空留的空间时不定的。主要满足自身大小和地址成整数倍关系就能被快速读写。uint8_类型那所有地址都是整数倍,uin16_t 那就是2的倍数地址。uint32_t  就是4的倍数地址。以此类推。


举例

typedef struct
{
	uint8_t a;
	uint16_t b;
	uint32_t c;
}type1;

typedef struct
{
	uint32_t a;
	uint16_t b;
	uint8_t  c;
}type2; 

根据规则1,type1和type2中最大的变量时4字节,所以type1,type2大小就是4的倍数,并且分配时4字节对齐。

type1.a起始地址就是4字节对齐的。 假设为type1.a地址为0x00。正常下一个地址是0x01.但是type1.b是2字节宽的数据。所以不能放在0x01地址,空一格位置。type1.b地址定位0x02.占用2字节。当前地址为0x04,刚好是4的倍数,可以做为type1.c的首地址。

type2中的a,b,c变量按照规则2都在连续的空间上,但是按照规则1,type2大小必须是4的倍数,所以type2.c之后预留了一个空的位置。

### C语言结构体对齐的原理和方法 #### 一、结构体对齐的基本概念 在C语言中,为了提高数据访问效率以及满足硬件架构的要求,编译器会对结构体中的成员进行内存对齐处理。这种对齐遵循一定的规则,主要包括以下几个方面: 1. **第一个成员的位置** 结构体的第一个成员始终存储在其分配空间的起始地址上[^2]。 2. **其他成员的对齐规则** 对于后续成员,它们会被放置在一个特定数值(称为对齐数)的整数倍地址处。这个对齐数通常是该成员类型的大小或者编译器指定的默认对齐值两者之间的最小值。 3. **填充字节的作用** 如果某个成员无法直接放在下一个可用位置而违反上述规则,则会通过插入额外的填充字节来调整其位置,从而实现正确的对齐[^3]。 4. **整体大小调整** 整个结构体最终占据的空间也需是对其中最宽基本类型成员大小的一个整数倍;如果必要的话,在最后一个字段后面还会增加一些尾部填充以达到这一目标。 #### 二、影响因素分析 - **平台差异** - 不同的操作系统可能有不同的默认对齐策略。例如,在Windows环境下使用Microsoft Visual Studio开发工具链时,默认情况下采用的是8字节边界对齐模式;而在大多数Linux发行版里则倾向于更紧凑些即按4字节单位排列。 - **自定义指令控制** - 开发者可以利用预处理器命令`#pragma pack(n)`来自行设定新的打包参数n (通常为1,2,4,...),以此改变当前作用域内的所有新声明对象内部组件间的间隔程度[#1]. #### 三、实例解析 考虑以下代码片段及其运行结果解释: ```c #include<stdio.h> #pragma pack(8)//交代按照8字节对齐宽度 struct A { char x;//占用1字节 char y; char z; }; struct B { char x; int y;//占用4字节 char z; }; int main(){ struct A a; struct B b; printf("%d\n",sizeof(a)); // 输出3 printf("%d\n",sizeof(b)); // 输出12 return 0; } ``` 对于结构A而言,由于三个字符型变量连续分布并无任何中间隔开的需求所以实际消耗正好等于各自长度之和也就是3字节[^1]。 然而针对B来说情况变得复杂起来因为存在不同类型的数据混合在一起的情况: - 首先是单字节的'x',它自然位于偏移量为零的地方; - 接下来的'int y'(四字节),考虑到前面已经用了1个字节因此这里需要补齐至最近合适的四位点再开始计数故产生了三个空闲位使得y真正意义上的起点应该是第四个单元格而非第二个; - 最后的另一个单独存在的char 'z'同样也要顾及之前留下的空白进而再次跳过直至找到适合自己的八分之一界限才正式安顿下来再加上必要的结尾补充总共构成了十二个有效区域^. #### 四、总结说明 综上所述,C编程环境里的复合资料形式——结构体并非简单地把各个组成部分堆叠起来就算完成而是经过精心设计过的布局过程旨在兼顾性能表现的同时又能保持良好的可移植特性.[^2] ---
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值