c语言对齐方式研究笔记

c语言对齐方式研究笔记

为什么要对齐

TragicJun 发表于 2006-9-18 9:41:00 现代计算机中内存空间都是按照byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定类型变量的时候经常在特定的内存地址访问,这就需要各种类型数据按照一定的规则在空间上排列,而不是顺序的一个接一个的排放,这就是对齐。
对齐的作用和原因:各个硬件平台对存储空间的处理上有很大的不同。一些平台对某些特定类型的数据只能从某些特定地址开始存取。比如有些架构的CPU在访问一个没有进行对齐的变量的时候会发生错误,那么在这种架构下编程必须保证字节对齐.其他平台可能没有这种情况,但是最常见的是如果不按照适合其平台要求对数据存放进行对齐,会在存取效率上带来损失。所谓内存对齐,是为了让内存存取更有效率而采用的一种编译阶段优化内存存取的手段。
比如对于int x;(这里假设sizeof(int)==4),因为cpu对内存的读取操作是对齐的,如果x的地址不是4的倍数,那么读取这个x,需要读取两次共8个字节,然后还要将其拼接成一个int,这比存取对齐过的x要麻烦很多。(比如操作系统一次允许读4个字节,从内存4-7存放的是一个int,此时如果从地址4开始读,恰好可以读出一个int,但是如果从地址3开始读,需要读两次,然后进行拼接,形成一个int)。

怎么对齐

结构体对齐可以分为两种情况,结构体对齐和结构体内成员对齐

  • 结构体对齐

结构体对齐使用 **__attribute__((aligned(n)))**伪指令来设置结构体n字节对齐,在嵌入式中FATFS一般会要求缓冲区512字节对齐。

有以下结构体

typedef struct
 {
   char a;
   int b;
 } pack4_t  __attribute__((aligned(512)));

使用 __attribute__((aligned(512)))伪指令来设置结构体512字节对齐,则a的地址一定是512的倍数。

  • 结构体内成员对齐

结构体内成员对齐使用 , 一般联合体或者结构体强制转化时,需要注意结构体成员对齐信息
#pragma pack(n) //n字节对齐
/* 用户代码 */

#pragma pack() //恢复默认对齐
伪指令来设置结构体内成员n字节对齐

有以下结构体

typedef struct
 {
   char a;
   int b;
 } pack4_t;

在MDK默认情况下,分配内存时,a时char类型,占一个字节,所以对对齐没有要求在a分配内存后,会接着a的内存地址对b进行内存分配。
由于b是int类型,占4个字节,所以默认会4字节对齐,也就是b的地址应该是4的倍数,如果a挨着的地址不是4的倍数,则会向后顺延,直至地址为4的倍数,然后将对应的地址空间分配给b,a和b中间的地址空间则为空的,其他的类似。

在这里插入图片描述

#pragma pack(1) //1字节对齐  
typedef struct
 {
   char a;
   int b;
 } pack4_t;
 #pragma pack()  //恢复默认对齐 

使用1字节对齐时,a时char类型,占一个字节,所以对对齐没有要求在a分配内存后,会接着a的内存地址对b进行内存分配。
由于b是int类型,占4个字节,默认会4字节对齐,但是用户指定了使用1字节对齐,所以b也使用1字节对齐,也就是b的地址应该紧挨着a,其他的类似。

在这里插入图片描述

#pragma pack(2) //2字节对齐  
typedef struct
 {
   char a;
   int b;
 } pack4_t;
 #pragma pack()  //恢复默认对齐 

使用1字节对齐时,a时char类型,占一个字节,所以对对齐没有要求在a分配内存后,会接着a的内存地址对b进行内存分配。
由于b是int类型,占4个字节,默认会4字节对齐,但是用户指定了使用2字节对齐,所以b使用2字节对齐,也就是b的地址应该是2的倍数,如果a挨着的地址不是2的倍数,则会向后顺延,直至地址为2的倍数,然后将对应的地址空间分配给b,a和b中间的地址空间则为空的,其他的类似。

在这里插入图片描述

#pragma pack(4) //4字节对齐  
typedef struct
 {
   char a;
   int b;
 } pack4_t;
 #pragma pack()  //恢复默认对齐 

使用1字节对齐时,a时char类型,占一个字节,所以对对齐没有要求在a分配内存后,会接着a的内存地址对b进行内存分配。
由于b是int类型,占4个字节,所以默认会4字节对齐,也就是b的地址应该是4的倍数,如果a挨着的地址不是4的倍数,则会向后顺延,直至地址为4的倍数,然后将对应的地址空间分配给b,a和b中间的地址空间则为空的,这里看起来和MDK默认的分配一样,但是当b为double类型,占8个字节时,当使用默认分配时,b的地址需要是8的数倍,而这里只需要是4的倍数。

在这里插入图片描述

为了更加清楚理解,这里让结构体更复杂点

typedef struct
 {
   char a;
   int b;
   char c;
   uint16_t d;
   int e;
   char f;
   double g;
 } pack4_t;

在这里插入图片描述

在STM32上实测

#pragma pack(4)
typedef struct
 {
   char a;
   int b;
   char c;
   uint16_t d;
   int e;
   char f;
   double g;
 } pack4_t __attribute__((aligned(512)));
#pragma pack()
 
#pragma pack(2)
typedef struct
 {
   char a;
   int b;
   char c;
   uint16_t d;
   int e;
   char f;
   double g;
 } pack2_t __attribute__((aligned(512)));
#pragma pack()
 
#pragma pack(1)
typedef struct
 {
   char a;
   int b;
   char c;
   uint16_t d;
   int e;
   char f;
   double g;
 } pack1_t __attribute__((aligned(512)));
#pragma pack()
 
typedef struct
 {
   char a;
   int b;
   char c;
   uint16_t d;
   int e;
   char f;
   double g;
 } pack_t __attribute__((aligned(512)));

pack4_t  pack4  ;
pack2_t  pack2  ;
pack1_t  pack1  ;
pack_t  pack  ;

int main(void)
{    
  printf("\r\n1字节对齐    %x  %x  %x  %x  %x  %x  %x\r\n",&pack1.a, &pack1.b, &pack1.c, &pack1.d, &pack1.e , &pack1.f, &pack1.g);
  printf("\r\n2字节对齐    %x  %x  %x  %x  %x  %x  %x\r\n",&pack2.a, &pack2.b, &pack2.c, &pack2.d, &pack2.e , &pack2.f, &pack2.g);
  printf("\r\n4字节对齐    %x  %x  %x  %x  %x  %x  %x\r\n",&pack4.a, &pack4.b, &pack4.c, &pack4.d, &pack4.e , &pack4.f, &pack4.g);
  printf("\r\nMDK默认对齐  %x  %x  %x  %x  %x  %x  %x\r\n",&pack.a, &pack.b, &pack.c, &pack.d, &pack.e , &pack.f , &pack.g);
 
  while(1)
  {
    
  }
}

打印结果

1字节对齐    20005600  20005601  20005605  20005606  20005608  2000560c  2000560d

2字节对齐    20005800  20005802  20005806  20005808  2000580a  2000580e  20005810

4字节对齐    20005a00  20005a04  20005a08  20005a0a  20005a0c  20005a10  20005a14

MDK默认对齐  20005400  20005404  20005408  2000540a  2000540c  20005410  20005418
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值