理解结构体内存对齐并算清楚结构体大小,结构体对齐的意义在哪?

结构体对齐

1. 为什么存在结构体对齐呢?

结构体内存对齐的原因:

1.平台原因(移植原因):        

2.效率原因: 

2. 结构体对齐规则

 重点来了,知道了这些规则后,具体要怎么算一个结构体的大小呢?

结构体嵌套

结构体对齐的意义:


1. 为什么存在结构体对齐呢?

结构体内存对齐的原因:

1.平台原因(移植原因): 
       

        不是所有的硬件平台都能访问任意地址上的任意数据;某些硬件平台只能在某些特定地址处取某些特定的数据,否则就会抛出硬件异常。也就是说在计算机在内存读取数据时,只能在规定的地址处读数据,而不是内存中任意地址都是可以读取的。


2.效率原因: 

        正是由于只能在特定的地址处读取数据,所以在访问一些数据时,对于访问未对齐的内存,处理器需要进行两次访问;而对于对齐的内存,只需要访问一次就可以。 其实这是一种以空间换时间的做法,但这种做法是值得的。


2. 结构体对齐规则

        1.第一个成员在结构体变量偏移量为0的地址处,也就是第一个成员必须从头开始。
        2.其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。对齐数为编译器默认的一个
对齐数与该成员大小中的较小值。在64位编译器中默认值是8 ,可以在代码前通过#pragmapack (N)修改,使用#pragma pack(show)可以查看对齐值,但修改时N的取值只能设置成(1,2,4,8,16)这几个值
        3.结构体总大小为最大对齐数的整数倍。(每个成员变量都有自己的对齐数)
        4.如果嵌套结构体,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是
所有最大对齐数(包含嵌套结构体的对齐数)的整数倍。

        不同位的编译器不同类型所占字节不一样,如下图所示。注:以下数据类型对应表供参考,绝大部分是对应的,但具体大小以所用编译器为准!

但都会满足如下条件:

sizeof(char) == 1
sizeof(char) <= sizeof(short) <= sizeof(int) <= sizeof(long) <= sizeof(long long)

 重点来了,知道了这些规则后,具体要怎么算一个结构体的大小呢?

来了,从小白开始,教你们怎么算,这个面试可能会考,特别是c语言岗位的

以下面的这个结构体为例:

typedef struct test
{
    char a;
    int b;
    int d;  
    long c;
    int *e;

}test_t; 

我们根据对齐规则的第二点,要知道对齐数,具体怎么做呢,在代码上方加入这个

#pragma pack(show)

如下所示,我在编译时就会警告,告诉我们对齐数是多少

 知道对齐数了,还要知道每个类型的大小,代码如下

#include <stdio.h>
#pragma pack(show)

typedef struct test
{
    char a;
    int b;
    int c;  
    long d;
    int *e;
}test_t; 

int main()
{
    printf("short int的大小:%zd\n", sizeof(short int));
    printf("int      的大小:%zd\n", sizeof(int));
    printf("long	 的大小:%zd\n", sizeof(long));
    printf("long long的大小:%zd\n", sizeof(long long));
    printf("char	 的大小:%zd\n", sizeof(char));
    printf("char*	 的大小:%zd\n", sizeof(char*));
    printf("float	 的大小:%zd\n", sizeof(float));
    printf("double	 的大小:%zd\n", sizeof(double));
    printf("long double的大小:%zd\n", sizeof(long double));
    //printf("int的大小:%d", sizeof(int));

    //printf("结构体test的大小:%zd\n", sizeof(test_t));
    return 0;
}

会输出如下:和上面表格差不多,我使用的是64位编译器

short int的大小:2
int      的大小:4
long	 的大小:8
long long的大小:8
char	 的大小:1
char*	 的大小:8
float	 的大小:4
double	 的大小:8
long double的大小:16

        接下来该算结构体大小了,我知道了对齐数为8,要从结构体中定义的变量,从上往下算,我们要明确规则第三点中的每个成员变量都有自己的对齐数

        我们设结构体的内存存储从1开始,实际是从0开始的,为了易理解:

        计算结构体中第1个变量,a为char型,占1个字节。

        计算结构体中第2个变量,b为int型,占4个字节,根据规则对齐规则2(其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处),如果从2地址开始存储,是错误的,起始地址为2,无法整除下一个变量的第一个整型变量大小,2/4 != 0,如下图所示

        所以要从4地址开始存储,4/4 = 1,能整除,存储后,结构体现在大小为1+'3'+4=8;存储结构如下图所示,才是正确的

        计算结构体中第3个变量,c为int型,8/4=2,能整除,从8开始存储,占4个字节,结构体现在大小为8+4=12

        接下来计算结构体中第4个变量,d为long型,占8个字节,从12往后找能被8整除最近的数,就是16啦,所以从16开始存储,结构体现在大小为12+'4'+8=24

        接下来计算结构体中第5个变量,e为指针型,占8个字节,现在总大小为24+8=32,图就不画了,和上面的类似,所以这个结构体test,总共占用字节数为32

要是结构体中还是有第6个变量呢?如下所示

         有人说了,这不简单吗,+1不就得了么,总共33个字节。不是的!结构体对齐规则第三条中写着:结构体总大小为最大对齐数的整数倍,33显然不是,里33最近的就是8*5=40,那此时结构体大小就为40。

        我打印下结构体中的每个变量地址分配,就知道了,如下图所示,表示的是每个变量存储的地址起点。

 这和我上述分析是一样的,只不过我用的是10进制分析的,编译器是16进制,你们可以算一下。

          有人说了,这不是浪费空间吗,结构体中就多了一个字节的变量,结构导致整个结构体大小多了一个对齐数=8位。确实会这样,这个结构体需要优化,后面会讲,先说一下结构体中的特殊情况。

结构体嵌套

        就是结构体中嵌套了结构体,也就是套娃,这时候要怎么算呢?我这里用一个简单的例子讲一下,结构体里面套一个结构体。如下所示

 这时候要怎么算呢?

        我们刚刚算到c这里时,这时已经占了12个字节了,接下来到结构体这个对象s了,先算这个嵌套结构体的大小,这个结构体比较简单,第一个是名字的cha*型指针,我的是64位编译器,所以所占字节位8位,第二个位int型变量,所占字节数位4,现在总大小为12byte,再根据结构体对齐规则第三条,结构体总大小为最大对齐数的整数倍,里12最近的就是8*2=16,所以总字节数为16.

        嵌套结构体的大小为16,在结构体test怎么存储呢?此时16已经超过最大对齐数,存储的话能够被最大字节数整除的下一个最近位置开始存储即可。刚刚算到c时,已经占到12这个位置了。所以要从16开始存储,一共占16位,所以总字节数为12+'4'+16=32;

        类型这种一个变量或者对象的大小超过最大对齐数的很多,如string类型的变量,就有固定的大小32位。存储的话,像上面那样套路存储计算,即可;

        接下来计算结构体中第5个变量,本身占8个字节,按上面的套路计算即可,加上去后此时总字节数40;

        计算第六个变量,d为long型,占8个字节,从40开始存储,现在总字节数为48;

        计算第七个变量,d为char型,占1个字节,从41开始存储,现在总字节数为49 ,根据结构体对齐规则3(结构体总大小为最大对齐数的整数倍 ),所以最终结构体总字节数为56.   

地址分布如下图所示:

         C语言中还有一种情况:结构体中有一种如char  name[16],这种c语言字符串类型的,怎么算呢,这种很简单,他就相当于char 型的字符,有16个,连续排在一起。如下图所示,这种char型一个字符的,基本就是有地方就插,毕竟1能被任何数整除。

        结构体如何计算的已经搞定了,像上面说的,很多时间,结构体中的变量存储会有很多空隙,导致浪费存储空间,怎么办呢?

        可能很多人都想到了,把占用字节数小的变量放前面,大的放后面。

如果没有考虑,对结构体对齐不了解的话,可能变量乱放,如下图所示,这个结构体test的大小为72字节

         如果有考虑,对结构体对齐了解的话,变量不乱放,对结构体的成员位置进行优化,如下图所示,此时这个结构体test的大小为56字节,比不优化的少了16个字节

 

         可能有人会说:不优化结构体,平时写代码时运行时也没什么感觉

结构体对齐的意义:

        其实,在嵌入式领域,结构体是经常用到的,如单片机,stm32等,为什么要优化结构体呢?

        1.嵌入式领域的芯片,他的容量是很小的,不能我们电脑的磁盘那么大,可以节省空间。

        2.芯片中的数据读取,是有特定区域的,如果不优化结构体,结构体的数据读取的时候有些要拼接,这就浪费了时间,而芯片这种东西,效率是要求很高的。可以提高读取效率

        对于软件开发人员,不像嵌入式的芯片,很多软件并不在意这点运行所用的空间和时间,这是个细节吧。得要有这个意识,可能面试时问到,扣你细节时,回答对了,offer就是你的!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值