C语言之内存对齐数

版权声明:一起来探索代码的世界 https://blog.csdn.net/qq_34328833/article/details/51352663

    C语言之内存对齐:

       相信大家都看过金庸的武侠小说,小说里那些大侠凭借一门绝世武功就可以打遍天下无敌手,而我们今天要讲的内存对齐术就相当于武侠小说里的绝世武功,理解它就可以了解结构体是如何在内存中存储的啦!本文就以结构体中的内存对齐来理解什仫是内存对齐术?为什仫要进行内存对齐?要解决这两个问题,首先我们先来看三个简单的例子:

      实例一:

      

<span style="font-size:18px;">#include<stdio.h>
#include<stdlib.h>
typedef struct A
{
	int i;
	char c;
	char c2;
}A;
int main()
{
	printf("%d\n",sizeof(A));
	system("pause");
	return 0;
}</span>
      实例二:

      

<span style="font-size:18px;">#include<stdio.h>
#include<stdlib.h>
typedef struct A
{
	char c;
	int i;
	char c2;
}A;
int main()
{
	printf("%d\n",sizeof(A));
	system("pause");
	return 0;
}</span>
       实例三:

        

<span style="font-size:18px;">#include<stdio.h>
#include<stdlib.h>
typedef struct A
{
	char c;
	char c2;
	int i;
}A;
int main()
{
	printf("%d\n",sizeof(A));
	system("pause");
	return 0;
}</span>
      通过观察这三个结构体,我们发现他们具有相同的变量, 唯一的区别就是他们的变量的位置不同, 那仫这三个结构体的大小是不是相同呢?如果你认为一样大那就大错特错了难过,想必结果要让你大吃一惊:8  12  8 ,不信?疑问那就让我细细道来:这就是我们今天必须要理解清楚的一个概念,那就是 内存对齐。 

    问题一:为什仫要内存对齐?

    我们知道的是计算机中最小的存储单位是字节(一个字节是八个比特位),当我们查找数据的时候可以一个字节一个字节的查找,但是这种查找方式存在缺陷:查找速度慢。如果不存在内存对齐,在实例三中,我们首先为结构体变量c分配一个字节,然后紧着我们为结构体变量c2分配一个字节,在最后我们为结构体整形变量i分配四个字节;如果我们一个字节一个字节查找元素的话,找c和c2当然很容易了,可是找i呢?我们知道内存为整形变量i分配了4个字节,我们要访问完整的i我们需要找到变量c2之后的四块存储空间,和我们刚开始定义的一样我们是一个字节一个字节找,此时指针需要移动三次才可以找到全部整形变量i的所有字节。是不是觉得很麻烦呢?如果有了内存对齐呢?我们可以四个字节四个字节的找,是不是就提高了我们查找的速率呢?那是肯定的啦!得意所谓的内存对齐就是牺牲了部分空间来换取效率的方法。

   

      问题二:内存对齐有哪些规则?

        1).结构体变量的首地址能够被其最宽基本类型成员的大小所整除,结构体的第一个成员永远放在零偏移处;
        2).结构体每个成员相对于结构体首地址的偏移量都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字符;
        3). 结构体的总大小为结构体最宽基本类型成员大小和编译器缺省対界条件大小中比较小得那个值的整数倍,如有需要编译器会在最后一个成员之后加上填充字节;

       4).在这里我们是可以修改默认对齐数的,一般我们用 #progma pack(n)这个语句来设置默认对齐数, 在这个我设置的默认对齐数是n,读者可根据自己的需要自行设置对齐数;用#progma pack()来取消设置对齐数, 换句话说就是我们可以自己修改内存中的默认对齐数;

      通过上述几个规则大家是不是对内存对齐有了更深的理解呢?下面我就来画图理解上述三个类似的范例:

      范例一图:

       

        有兴趣的同学可以自己画图理解范例二和范例三,原理都是一样的,只要找出这个结构体对齐数,偏移数,最终的结果是这个最大对齐数的整数倍就可以了;而且在内存分配的过程中不能被忽视的一点就是环境,在不同平台上它的默认对齐数是不同的,结果也可能不同,但是我们可以用语句#progma pack(n)来修改你的默认对齐数那就不管你在什仫平台上都是可以达到你想要的结果啦!吐舌头

       

      几个特殊的实例分析:

       以下所举范例都是在.cpp文件,VS2010系统下操作的,在这里提前说明一下,方便小伙伴们理解:微笑

      范例一:所求结构体不存在任何变量,代码如下:

      

<span style="font-size:18px;">#include<stdio.h>
#include<stdlib.h>
typedef struct A
{
}A;
int main()
{
	printf("%d\n",sizeof(A));  //1
	system("pause");
	return 0;
}</span>
      此时这个结构体的大小是多少呢?在当前平台下我们知道它的大小是1,看到这里大家就疑惑了,为什仫它没有变量内存依然为它分配空间呢?疑问,我们知道的是虽然A这个结构体它没有任何的变量,可是系统可不这样认为,系统会想只要有你这仫个名字,我就为你分配一个字节;试想一下如果内存没有分配这个字节,如果有另一个变量它就会接着你之前分配的空间继续向后分配空间,如果我们有一个指针指向它,那仫这块空间指的是结构体A呢?还是那另一个变量呢?所以系统一般情况下为这种情况分配一个字节,便于管理,不至于出现上述问题;那仫是不是所有的环境下都是分配一个字节呢?在这里提供一种环境在linux下这种情况是不为结构体分配字节的,有兴趣的同学可以下去自己研究。

      范例二:结构体中嵌套结构体

      1).

<span style="font-size:18px;">#include<stdio.h>
#include<stdlib.h>
typedef struct B
{
	char c;
	double d;
}B;
typedef struct A
{
	int i;
	char a[5];
	B b;
}A;
int main()
{
	printf("%d\n",sizeof(A));  //32=4+5+(1+8)+7+7
	system("pause");
	return 0;
}</span>

                 这个结构体的大小又是多少呢?这里我们就要用我们之前讲到的内存对齐去分析它了,分析如下图:

          

         2).

<span style="font-size:18px;">#include<stdio.h>
#include<stdlib.h>
typedef struct A
{
	int i;
	char a[5];
	struct B
	{
		char c;
		double d;
	}b;
}A;
int main()
{
	printf("%d\n",sizeof(A));  //32
	system("pause");
	return 0;
}</span>

        这个类型的结构体大小又是多少呢?通过检测我们知道它也是32,在这里我就不一一列举了,有兴趣的读者可自行画图分析。 

    范例三:结构体的最后一个元素是未知大小的数组,在这里我们不得不提出一个概念: 柔性数组

                  柔性数组:  结构体中的最后一个元素允许是未知大小的数组

                  

#include<stdio.h>
#include<stdlib.h>
typedef struct A
{
	int n;
	int data[0];  //可变长度的数组
}A;
int main()
{
	printf("%d\n",sizeof(A));  //4
	system("pause");
	return 0;
}
        这个结构体的大小是多少呢?通过测试我们发现它的大小是4,系统好像并没有为数组data[]分配空间,如果我们给数组data[]分配空间后又会是什仫结果呢?请看下面一个例子:

        

#include<stdio.h>
#include<stdlib.h>
typedef struct A
{
	int n;
	int data[0];  //可变长度的数组
}A;
int main()
{
	A *a=(A *)malloc(sizeof(A)+10*sizeof(int));
	printf("%d\n",sizeof(A));  //4
	free(a);
	system("pause");
	return 0;
}


           在这个为柔性数组分配了空间,即10个整形的结构体的大小是多少呢?通过测试我们发现它的大小还是4,这就令人觉得困惑了?明明分配了空间啊!为什仫测试不出来呢?这就要从柔性数组的特性说起了: 在定义结构体的时候我们就知道柔性数组只是一个允许是未知大小的数组,它是用来扩展结构体大小的,它自己和结构体并没有什仫关系,只是我们在使用柔性数组的时候需要把它当做结构体的一个成员,说的通俗点,柔性数组其实并不占结构体的内存大小, 所以即使我们为结构体分配了大小它依然是4个字节。

        相信自己是最棒的,加油!

       

      ,

展开阅读全文

没有更多推荐了,返回首页