C语言 | 关于结构体内存对齐,看这篇就够了

Posted 嵌入式大本营

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C语言 | 关于结构体内存对齐,看这篇就够了相关的知识,希望对你有一定的参考价值。

大家好,今天分享的是面试过程中可能遇到的一道经典问题,就是结构体是如何对齐的,以及结构体占用多少个字节。另外,公众号有了讨论区,相当于是之前的留言功能,欢迎在讨论区提出看法。

C语言当中的结构体内存对齐基本上是笔试中必考的问题,一般都是给你一个结构体,问你这个结构体占用多少个字节。今天就来深入分析一下可能涉及到的各种情况。
首先来说一下结构体的对齐规则。
一、结构体对齐规则首先要看有没有用 #pragma pack宏声明,这个宏可以改变对齐规则,有宏定义的情况下结构体的自身宽度就是宏上规定的数值大小,所有内存都按照这个宽度去布局(这样说其实不太严谨,后面会提到),#pragma pack 参数只能是 '1', '2', '4', '8', or '16'。
二、在没有 #pragma pack这个宏的声明下,遵循下面三个原则:
1、第一个成员的首地址为0.
2、每个成员的首地址是自身大小的整数倍
3、结构体的总大小,为其成员中所含最大类型的整数倍。

下面我们以32位机器为例,通过几个例子来详细说明。
struct test{ char a; //1 char b; //1  char c;  //1};
这个结构体占几个字节呢?答案是3个。你可能觉得很简单,每个变量都是占一个字节,三个当然加起来就是3了。
那如果把第二个变量改成short呢?
struct test{ char a; //1 short b; //2 char c; //1};
如果你觉得是4个,那就错了。答案是6个。
来结合对齐规则来看一下,1、第一个成员首地址为0(准确说是偏移量),这个没什么好说, 2、每个成员的地址是自身大小的整数倍,因为b是short类型的,占用两个字节,所以,必须以2字节对齐,也就是说你可以把b放在0啊,2啊,4啊这些地址,但是你不能放在1,3,5这样的地址。a的地址是0,下一个地址是1,不能放,只能空掉,放在2位置处。这样,a和b就占了4个字节了,接下来c占一个字节。但是,还没完,看第三条规则, 结构体的总大小,为其成员中所含最大类型的整数倍。所以,在这个例子中,结构体总大小应该要为2的整数倍,所以是6,而不是5。

再看这个,分析和前面是一样的,答案是12.
struct test{ char a; //1+3 int b; //4 short c; //2+2};

C语言 | 关于结构体内存对齐,看这篇就够了

再看一个
struct test{  char d[7];  double a; short b; char* c;};
这个答案是24,这里只要注意数组分析是一样的,7个应该要补成8个字节,还有就是最后所有的指针在32位系统中都是占4个字节。

前面讲的都是没有用 #pragma pack 这个宏声明的情况,接下来分析一下如果有这个宏声明有什么不同。
#pragma pack(1)struct test{ char a;  int  b;};
根据我们之前的分析,这个结构体应该要占用8个字节,但是因为加了
#pragma pack(1)
导致整个结构体按照1字节来对齐,所以结果是5,不再是8.

C语言 | 关于结构体内存对齐,看这篇就够了

如果是 # pragma  pack(2)呢?相信大家都能想到答案是6.

C语言 | 关于结构体内存对齐,看这篇就够了

但是一定是按照这个宏声明来对齐吗?不一定。比如:
#pragma pack(8)struct test{ char a; int b; short c;};
按照分析,使用宏强制8字节对齐之后,最后的c应该是占8字节,一共是16个字节,但是结果是12,也就是说编译器没有听你的,它认为最大的数据类型长度是4,所以按4就行了,而不必按8.

C语言 | 关于结构体内存对齐,看这篇就够了

因此,我们对第一条规则进行修正,严格来说,是按照这个宏声明的和实际数据类型中最大值较小的那个来决定。
就这个例子中,里面最大的是int,长度是4,而宏声明是8,因此取4.如果你的宏声明比4小,那就按声明的来。

最后还有就是位域的相关知识。在32位cpu上选择缺省对齐的情况下,有如下结构体定义:
struct test{ char a : 7; int b : 11; int c : 4; int d : 10; char index; };
对于这个结构体,是占几个字节呢?你可能会觉得是20个字节,那就错了。这种其实是位域,比如a,只占char类型的7位,并没有占8位,后面的b,c,c也是只占int的几个位,因此可以共用,11+4+10=25,没有超过32,因此占4个字节就够了。然后前后的两个char根据对齐规则各占一个字节。

以上基本把可能出现的情况都涉及到了。总的来说就是,首先看有没有宏声明,如果没有,就牢牢把握那三条规则就行了。如果有,还得看这个宏声明的长度是不是比结构体成员最大类型的长度还要长,如果是的话,那这个声明是无效的。

那顺便再看这个东西。
struct test{ int a; char b; char c; }A;
union MyUnion{ int a; long b; int c;}B;
这两个那个大,如果你觉得B大,那就掉坑里了,B是一个共用体,它的长度只取决于最大的那个成员的长度。A的大小是8,而B只有4.

好了,以上就是关于结构体对齐的内容,关于结构体对齐,还有什么想说的,可以在下方留言。
嵌入式大本营 发起了一个读者讨论 关于结构体还有哪些注意的地方? 精选讨论内容