龙空技术网

结构体对齐

嵌入式底层分享 580

前言:

此刻同学们对“c语言结构体内存对齐原则”大体比较关怀,朋友们都想要了解一些“c语言结构体内存对齐原则”的相关文章。那么小编同时在网络上汇集了一些有关“c语言结构体内存对齐原则””的相关内容,希望看官们能喜欢,大家一起来了解一下吧!

什么是字节对齐?

计算机中内存大小的基本单位是字节(byte),但是实际上计算机非逐字节大小去读写内存,而是以2,4,8的倍数字节块来读写内存,所以他的地址必须是2,4,8的倍数,就是要求各种数据类型按照一定的规则在空间上排列,这就是对齐

为什么字节对齐?

无论是数据是否对齐,计算机都能正常运行,比如某结构体大小是11字节,最后占用了16字节,很明显是浪费了空间,最重要的是提高系统系统要求,以空间换时间。

注意:有些处理器遇到未对齐的数据,可能不能够正常工作。

跨平台通信

由于不同平台对齐方式可能不同,如此一来,由于同样的结构在不同平台大小其大小可能不同,在无意识的情况下,互相放松的数据可能会出现错误,甚至引发严重的问题。因此,为了不同处理器之间能够正确的处理消息,我们有两种处理方法:

1字节对齐自己对结构体进行字节填充

我们可以使用#pragma pack(n) (n为字节对齐数)使得结构体一字节对齐

 #pragma pack(1)struct test{   int a;   char b;   int c;   short d;};#pragma pack();//还原默认对齐

#pragma pack(1)下,任何平台结构体test的大小都为11字节,这样做能够保证跨平台的结构大小一致,同时还节省了空间,但不好的是降低了效率。

如下的方法,也是使其1字节对齐

struct test{    int a;    char b;    int c;    short d;}__attribute__((packed));

注:

__attribute__((aligned(n))),让所作用的结构成员对齐在n字节自然边界上。如果结构中有成员的长度大于n,则按照最大成员的长度来对齐__attribute__((packed)),取消结构在编译过程中的优化对齐,也可以认为是1字节对齐。

总结:

虽然我们不需要具体关心字节对齐的细节,但是如果不关注字节对齐的问题,可能会在编程中遇到难以理解或解决的问题。因此针对字节对齐,总结了以下处理建议:

结构体成员合理安排位置,以节省空间跨平台数据结构可考虑1字节对齐,节省空间但影响访问效率跨平台数据结构人为进行字节填充,提高访问效率但不节省空间本地数据采用默认对齐,以提高访问效率32位与64位默认对齐数不一样

结构体内存对齐的demo:

typedef struct{    char a;    short b;    char c;    int d;    char e;}test_struct;int main(void){    test_struct test_s;    printf("test_s addr = %#.8x\n",&test_s);    printf("test_s.a addr = %#.8x\n",&test_s.a);    printf("test_s.b addr = %#.8x\n",&test_s.b);    printf("test_s.c addr = %#.8x\n",&test_s.c);    printf("test_s.d addr = %#.8x\n",&test_s.d);    printf("test_s.e addr = %#.8x\n",&test_s.e);    printf("sizeof(test_s) = %d\n",sizeof(test_s));    return 0;}

什么是字节对齐?

计算机中内存大小的基本单位是字节(byte),但是实际上计算机非逐字节大小去读写内存,而是以2,4,8的倍数字节块来读写内存,所以他的地址必须是2,4,8的倍数,就是要求各种数据类型按照一定的规则在空间上排列,这就是对齐

为什么字节对齐?

无论是数据是否对齐,计算机都能正常运行,比如某结构体大小是11字节,最后占用了16字节,很明显是浪费了空间,最重要的是提高系统系统要求,以空间换时间。

注意:有些处理器遇到未对齐的数据,可能不能够正常工作。

跨平台通信

由于不同平台对齐方式可能不同,如此一来,由于同样的结构在不同平台大小其大小可能不同,在无意识的情况下,互相放松的数据可能会出现错误,甚至引发严重的问题。因此,为了不同处理器之间能够正确的处理消息,我们有两种处理方法:

1字节对齐自己对结构体进行字节填充

我们可以使用#pragma pack(n) (n为字节对齐数)使得结构体一字节对齐

 #pragma pack(1)struct test{   int a;   char b;   int c;   short d;};#pragma pack();//还原默认对齐

#pragma pack(1)下,任何平台结构体test的大小都为11字节,这样做能够保证跨平台的结构大小一致,同时还节省了空间,但不好的是降低了效率。

如下的方法,也是使其1字节对齐

struct test{    int a;    char b;    int c;    short d;}__attribute__((packed));

注:

__attribute__((aligned(n))),让所作用的结构成员对齐在n字节自然边界上。如果结构中有成员的长度大于n,则按照最大成员的长度来对齐__attribute__((packed)),取消结构在编译过程中的优化对齐,也可以认为是1字节对齐。

总结:

虽然我们不需要具体关心字节对齐的细节,但是如果不关注字节对齐的问题,可能会在编程中遇到难以理解或解决的问题。因此针对字节对齐,总结了以下处理建议:

结构体成员合理安排位置,以节省空间跨平台数据结构可考虑1字节对齐,节省空间但影响访问效率跨平台数据结构人为进行字节填充,提高访问效率但不节省空间本地数据采用默认对齐,以提高访问效率32位与64位默认对齐数不一样

结构体内存对齐的demo:

typedef struct{    char a;    short b;    char c;    int d;    char e;}test_struct;int main(void){    test_struct test_s;    printf("test_s addr = %#.8x\n",&test_s);    printf("test_s.a addr = %#.8x\n",&test_s.a);    printf("test_s.b addr = %#.8x\n",&test_s.b);    printf("test_s.c addr = %#.8x\n",&test_s.c);    printf("test_s.d addr = %#.8x\n",&test_s.d);    printf("test_s.e addr = %#.8x\n",&test_s.e);    printf("sizeof(test_s) = %d\n",sizeof(test_s));    return 0;}

分析:

在分析这个问题之前,我们先记住关于结构体内存对齐的三条原则:

(1)结构体变量的起始地址能够被其最宽的成员大小被整除

(2)结构体每个成员相对于起始地址,如果不能则在前一个成员后面补充字节。

(3)结构体总体大小能够被最宽的成员的大小整除,如不能则后面补充字节。

分析做个问题我们就不考虑编译器可以指定对齐大小的情况了。在32bit环境中,一般默认的对齐大小是4.

test_a是结构体的首地址test_s一样的地址的地址相同都是0xb3971120,然后看test_s.b是short类型的字节大小为2,因为test_a是char类型,所以大小是1,所以偏移是1,所以1不能整除2,所以要空出一个字节地址,补充起来,就是0xb3971122地址。后面的成员是一样的。要如果不想浪费地址控制,那就尽量在程序里保证对齐,就是改变成员的顺序。

调整完顺序:

typedef struct{    char a;    char c;    short b;    int d;    char e;}test_struct;

结果是下图

在 Linux 内核中,我们经常看到 aligned 和 packed 一起使用,即对一个变量或类型同时使用 aligned 和 packed 属性声明。这样做的好处是,既避免了结构体内因地址对齐产生的内存空洞,又指定了整个结构体的对齐方式。

struct student{    char a;    short b;    int c;}__attribute__((packed,align(8)));    int main(void){    struct student s;    printf("%d\n",sizeof(s));    printf("%p\n",&(s.a));    printf("%p\n",&(s.b));    printf("%p\n",&(s.c)); }

编译结果:

总结结构体struct内存对齐的3大规则:

1.对于结构体的各个成员,第一个成员的偏移量是0,排列在后面的成员其当前偏移量必须是当前成员类型的整数倍;

2.结构体内所有数据成员各自内存对齐后,结构体本身还要进行一次内存对齐,保证整个结构体占用内存大小是结构体内最大数据成员的最小整数倍,在32bit环境中,一般默认的对齐大小是4;64bit环境中,一般默认对齐大小是8;

3.如程序中有#pragma pack(n)预编译指令或者__attribute__((aligned(n))),则所有成员对齐以n字节为准(即偏移量是n的整数倍),不再考虑当前类型以及最大结构体内类型。

另外谈下联合体union内存对齐,大家只要记住联合体union内存对齐的2大规则:

联合体union内存对齐的2大规则:

1.找到占用字节最多的成员;

2.union的字节数必须是占用字节最多的成员的字节的倍数,而且需要能够容纳其他的成员

typedef union {    long i;    int k[5];    char c;}union_type;int main(){    printf("%d",sizeof(union_type));    return 0;}

编译结果是:

根据第二条规则,要计算union的大小,首先要找到占用字节最多的成员,本例中是long,占用8个字节,int k[5]中都是int类型,仍然是占用4个字节的,然后union的字节数必须是占用字节最多的成员的字节的倍数,而且需要能够容纳其他的成员,为了要容纳k(20个字节),就必须要保证是8的倍数的同时还要大于20个字节,所以是24个字节

改变下联合体,看看这个大小

typedef union {    long i;    char c;}union_type;int main(){    printf("%d",sizeof(union_type));    return 0;}

编译结果是:

根据第一条规则得出,long是8字节, char是1字节,8字节大于1字节,且8字节是1字节的倍数,所以联合体的内存大小是8

如果文章有用的话,记得点赞关注

标签: #c语言结构体内存对齐原则