⑴ 下载程序时字节对齐
一、快速理解
1、内存对齐原则:
第一个成员的首地址为0.
每个成员的首地址是自身大小的整数倍
结构体的总大小,为其成员中所含最大类型的整数倍。
2、什么是字节对齐?
在C语言中,结构是一种复合数据类型,其构成元素既可以是基本数据类型(如int、long、float等)的变量,也可以是一些复合数据类型(如数组、结构、联合等)的数据单元。在结构中,编译器为结构的每个成员按其自然边界(alignment)分配空间。各个成员按照它们被声明的顺序在内存中顺序存储,第一个成员的地址和整个结构的地址相同。
为了使CPU能够对变量进行快速的访问,变量的起始地址应该具有某些特性,即所谓的”对齐”.
比如4字节的int型,其起始地址应该位于4字节的边界上,即起始地址能够被4整除.
3、 字节对齐有什么作用?
字节对齐的作用不仅是便于cpu快速访问。
同时合理的利用字节对齐可以有效地节省存储空间。
对于32位机来说,4字节对齐能够使cpu访问速度提高,比如说一个long类型的变量,如果跨越了4字节边界存储,那么cpu要读取两次,这样效率就低了。
但是在32位机中使用1字节或者2字节对齐,反而会使变量访问速度降低。所以这要考虑处理器类型,另外还得考虑编译器的类型。在vc中默认是4字节对齐的,GNU gcc 是默认4字节对齐。
4、 更改C编译器的缺省字节对齐方式
在缺省情况下,C编译器为每一个变量或是数据单元按其自然对界条件分配空间。一般地,可以通过下面的方法来改变缺省的对界条件:
使用伪指令#pragma pack (n),C编译器将按照n个字节对齐。
使用伪指令#pragma pack(),取消自定义字节对齐方式。
另外,还有如下的一种方式:
__attribute((aligned (n))),让所作用的结构成员对齐在n字节自然边界上。如果结构中有成员的长度大于n,则按照最大成员的长度来对齐。 ·
attribute ((packed)),取消结构在编译过程中的优化对齐,按照实际占用字节数进行对齐。
5、 举例说明
例1
struct test
{
char x1;
short x2;
float x3;
char x4;
};
1
2
3
4
5
6
7
1
2
3
4
5
6
7
由于编译器默认情况下会对这个struct作自然边界(有人说“自然对界”我觉得边界更顺口)对齐,结构的第一个成员x1,其偏移地址为0,占据了第1个字节。第二个成员x2为short类型,其起始地址必须2字节对界。
因此,编译器在x2和x1之间填充了一个空字节。结构的第三个成员x3和第四个成员x4恰好落在其自然边界地址上,在它们前面不需要额外的填充字节。
在test结构中,成员x3要求4字节对界,是该结构所有成员中要求的最大边界单元,因而test结构的自然对界条件为4字节,编译器在成员x4后面填充了3个空字节。整个结构所占据空间为12字节。
例2
#pragma pack(1) //让编译器对这个结构作1字节对齐
struct test
{
char x1;
short x2;
float x3;
char x4;
};
#pragma pack() //取消1字节对齐,恢复为默认4字节对齐
1
2
3
4
5
6
7
8
9
1
2
3
4
5
6
7
8
9
这时候sizeof(struct test)的值为8。
例3
#define GNUC_PACKED __attribute__((packed))
struct PACKED test
{
char x1;
short x2;
float x3;
char x4;
}GNUC_PACKED;
1
2
3
4
5
6
7
8
1
2
3
4
5
6
7
8
这时候sizeof(struct test)的值仍为8。
二、深入理解
①什么是字节对齐,为什么要对齐?
现代计算机中内存空间都是按照byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始。
但实际情况是在访问特定类型变量的时候经常在特定的内存地址访问,这就需要各种类型数据按照一定的规则在空间上排列,而不是顺序的一个接一个的排放,这就是对齐。
对齐的作用和原因:各个硬件平台对存储空间的处理上有很大的不同。一些平台对某些特定类型的数据只能从某些特定地址开始存取。比如有些架构的CPU在访问一个没有进行对齐的变量的时候会发生错误,那么在这种架构下编程必须保证字节对齐.其他平台可能没有这种情况,但是最常见的是如果不按照适合其平台要求对数据存放进行对齐,会在存取效率上带来损失。比如有些平台每次读都是从偶地址开始,如果一个int型(假设为32位系统)如果存放在偶地址开始的地方,那么一个读周期就可以读出这32bit,而如果存放在奇地址开始的地方,就需要2个读周期,并对两次读出的结果的高低字节进行拼凑才能得到该32bit数据。显然在读取效率上下降很多。
②字节对齐对程序的影响:
先让我们看几个例子吧(32bit,x86环境,gcc编译器):
设结构体如下定义:
struct A
{
int a;
char b;
short c;
};
struct B
{
char b;
int a;
short c;
};
1
2
3
4
5
6
7
8
9
10
11
12
1
2
3
4
5
6
7
8
9
10
11
12
现在已知32位机器上各种数据类型的长度如下:
char:1(有符号无符号同)
short:2(有符号无符号同)
int:4(有符号无符号同)
long:4(有符号无符号同)
float:4 double:8
那么上面两个结构大小如何呢?
结果是:
sizeof(strcut A)值为8
sizeof(struct B)的值却是12一、快速理解
1、内存对齐原则:
第一个成员的首地址为0.
每个成员的首地址是自身大小的整数倍
结构体的总大小,为其成员中所含最大类型的整数倍。
2、什么是字节对齐?
在C语言中,结构是一种复合数据类型,其构成元素既可以是基本数据类型(如int、long、float等)的变量,也可以是一些复合数据类型(如数组、结构、联合等)的数据单元。在结构中,编译器为结构的每个成员按其自然边界(alignment)分配空间。各个成员按照它们被声明的顺序在内存中顺序存储,第一个成员的地址和整个结构的地址相同。
为了使CPU能够对变量进行快速的访问,变量的起始地址应该具有某些特性,即所谓的”对齐”.
比如4字节的int型,其起始地址应该位于4字节的边界上,即起始地址能够被4整除.
3、 字节对齐有什么作用?
字节对齐的作用不仅是便于cpu快速访问。
同时合理的利用字节对齐可以有效地节省存储空间。
对于32位机来说,4字节对齐能够使cpu访问速度提高,比如说一个long类型的变量,如果跨越了4字节边界存储,那么cpu要读取两次,这样效率就低了。
但是在32位机中使用1字节或者2字节对齐,反而会使变量访问速度降低。所以这要考虑处理器类型,另外还得考虑编译器的类型。在vc中默认是4字节对齐的,GNU gcc 是默认4字节对齐。
4、 更改C编译器的缺省字节对齐方式
在缺省情况下,C编译器为每一个变量或是数据单元按其自然对界条件分配空间。一般地,可以通过下面的方法来改变缺省的对界条件:
使用伪指令#pragma pack (n),C编译器将按照n个字节对齐。
使用伪指令#pragma pack(),取消自定义字节对齐方式。
另外,还有如下的一种方式:
__attribute((aligned (n))),让所作用的结构成员对齐在n字节自然边界上。如果结构中有成员的长度大于n,则按照最大成员的长度来对齐。 ·
attribute ((packed)),取消结构在编译过程中的优化对齐,按照实际占用字节数进行对齐。
5、 举例说明
例1
struct test
{
char x1;
short x2;
float x3;
char x4;
};
1
2
3
4
5
6
7
1
2
3
4
5
6
7
由于编译器默认情况下会对这个struct作自然边界(有人说“自然对界”我觉得边界更顺口)对齐,结构的第一个成员x1,其偏移地址为0,占据了第1个字节。第二个成员x2为short类型,其起始地址必须2字节对界。
因此,编译器在x2和x1之间填充了一个空字节。结构的第三个成员x3和第四个成员x4恰好落在其自然边界地址上,在它们前面不需要额外的填充字节。
在test结构中,成员x3要求4字节对界,是该结构所有成员中要求的最大边界单元,因而test结构的自然对界条件为4字节,编译器在成员x4后面填充了3个空字节。整个结构所占据空间为12字节。
例2
#pragma pack(1) //让编译器对这个结构作1字节对齐
struct test
{
char x1;
short x2;
float x3;
char x4;
};
#pragma pack() //取消1字节对齐,恢复为默认4字节对齐
1
2
3
4
5
6
7
8
9
1
2
3
4
5
6
7
8
9
这时候sizeof(struct test)的值为8。
例3
#define GNUC_PACKED __attribute__((packed))
struct PACKED test
{
char x1;
short x2;
float x3;
char x4;
}GNUC_PACKED;
1
2
3
4
5
6
7
8
1
2
3
4
5
6
7
8
这时候sizeof(struct test)的值仍为8。
二、深入理解
①什么是字节对齐,为什么要对齐?
现代计算机中内存空间都是按照byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始。
但实际情况是在访问特定类型变量的时候经常在特定的内存地址访问,这就需要各种类型数据按照一定的规则在空间上排列,而不是顺序的一个接一个的排放,这就是对齐。
对齐的作用和原因:各个硬件平台对存储空间的处理上有很大的不同。一些平台对某些特定类型的数据只能从某些特定地址开始存取。比如有些架构的CPU在访问一个没有进行对齐的变量的时候会发生错误,那么在这种架构下编程必须保证字节对齐.其他平台可能没有这种情况,但是最常见的是如果不按照适合其平台要求对数据存放进行对齐,会在存取效率上带来损失。比如有些平台每次读都是从偶地址开始,如果一个int型(假设为32位系统)如果存放在偶地址开始的地方,那么一个读周期就可以读出这32bit,而如果存放在奇地址开始的地方,就需要2个读周期,并对两次读出的结果的高低字节进行拼凑才能得到该32bit数据。显然在读取效率上下降很多。
②字节对齐对程序的影响:
先让我们看几个例子吧(32bit,x86环境,gcc编译器):
设结构体如下定义:
struct A
{
int a;
char b;
short c;
};
struct B
{
char b;
int a;
short c;
};
1
2
3
4
5
6
7
8
9
10
11
12
1
2
3
4
5
6
7
8
9
10
11
12
现在已知32位机器上各种数据类型的长度如下:
char:1(有符号无符号同)
short:2(有符号无符号同)
int:4(有符号无符号同)
long:4(有符号无符号同)
float:4 double:8
那么上面两个结构大小如何呢?
结果是:
sizeof(strcut A)值为8
sizeof(struct B)的值却是12
⑵ 结构体内存对齐
结构体内存对齐
一、结构体对齐的三大原则
1、数据成员对齐规则:结构(struct)(或联合体(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小或者成员的子成员大小(只要该成员有子成员,比如说是数组、结构体等)的整数倍开始(比如int为4字节,则要从4的整数倍地址开始存储)
2、结构体作为成员:如果一个结构体里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储(struct a里存有struct b,b里面有char、int 、double等元素,那b应该从8的整数倍开始存储)
3、收尾工作:结构体的总大小,也就是sizeof的结果,必须是其内部最大成员的整数倍,不足的要补齐。
首先,我们看一下各个数据类型在不同操作系统中所占的位数
二、内存对齐规则的应用
图中输出结果是24,为什么是24呢?
a 为double类型,占8个字节,又因为是第一个成员,起始值对应offset为0的位置。又因为占8个字节,所以a对应的是偏移量为8的地址空间
b 为char类型,占1个字节,对齐到1的整数倍,也就是下一个地址空间。
c 为int类型,占4个字节,对齐到4的整数倍,对齐数为12,偏移量为4的地址空间
d 为short类型,占2个字节,对齐到2的整数倍,对齐数为16,偏移量为2的地址空间
结构体的总大小,必须是其内部最大成员的整数倍,不足的要补齐,struct1中最大的成员为double类型,那么结构体的总大小即为8的整数倍,目前已经占了17字节,所以总大小应为24个字节
我们再看一下下面的输出又是多少呢?
输出结果是16。
如图a不变
b 为int类型,占4个字节,对齐到4的整数倍,对齐数为8,偏移量为4的地址空间
c 为char类型,占1个字节,对齐到1的整数倍,也就是下一个地址空间。
d 为short类型,占2个字节,对齐到2的整数倍,对齐数为14,偏移量为2的地址空间
又因为内存对齐原则,所以结构体的总大小为16个字节
那么结构体是否可以嵌套使用呢,嵌套使用的话,内存又该如何计算呢?
这里的输出结果是多少呢。我们来计算一下
如图a不变
b 为int类型,占4个字节,对齐到4的整数倍,对齐数为8,偏移量为4的地址空间
c 为char类型,占1个字节,对齐到1的整数倍,也就是下一个地址空间。
d 为short类型,占2个字节,对齐到2的整数倍,对齐数为14,偏移量为2的地址空间
e 为int类型,占4个字节,对齐到4的整数倍,对齐数为16,偏移量为4的地址空间
已知struct1占24个字节,double 占8个字节,不知道为什么的请往上看。
结构体作为成员:如果一个结构体里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储(struct a里存有struct b,b里面有char、int 、double等元素,那b应该从8的整数倍开始存储)
所以struct1 应该从8的整数倍开始存储
那么struct1 占24个字节,对齐到8的整数倍,对齐数为24,偏移量为24的地址空间
所以结构体的总大小为48个字节
三、为什么要内存对齐呢?
(一)性能上的提升
从内存占用的角度讲,对齐后比未对齐有些情况反而增加了内存分配的开支,是为了什么呢?
数据结构(尤其是栈)应该尽可能地在自然边界上对齐,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。最重要的是提高内存系统的性能。
(二)跨平台
有些硬件平台并不能访问任意地址上的任意数据的,只能处理特定类型的数据,否则会导致硬件层级的错误。
有些CPU(如基于 Alpha,IA-64,MIPS,和 SuperH 体系的)拒绝读取未对齐数据。当一个程序要求这些 CPU 读取未对齐数据时,这时 CPU 会进入异常处理状态并且通知程序不能继续执行。
举个例子,在 ARM,MIPS,和 SH 硬件平台上,当操作系统被要求存取一个未对齐数据时会默认给应用程序抛出硬件异常。所以,如果编译器不进行内存对齐,那在很多平台的上的开发将难以进行。
⑶ 有经验的C语言程序常说的“内存对齐”,原因究竟是什么
在C语言程序开发中,有时有经验的程序员会提起“内存对齐”一词,事实上,这也是C语言中结构体的 size 不等于它所有成员 size 之和的原因(C语言中的结构体的size,并不等于它所有成员size之和,为什么?),那么,C语言程序为什么要“内存对齐”呢?
事实上,本节只是粗浅讨论,处理器的内存系统比这里描述的要复杂得多,涉及的内容也要复杂得多。不过,我们至少已经知道,在C语言程序中坚持内存对齐还是有很多好处的。
⑷ 结构体struct {int num; int age;char sex;}占多少个字节
说到结构体的内存占用,就必须要提到内存对齐的一个概念。
32位系统下,我们普遍在用的gcc编译器和vc编译器默认是按照4个字节的大小实现内存对齐的,也就是说结构体的内存分配默认是按照4个字节的倍数进行分配的。
对于结构体struct
{
int
num;
int
age;
char
sex;
}来说,int
num
占用4个字节,int
age占用4个字节
char
sex是一个字节长度,但是需要符合内存对齐原则,因此在内存分配的时候,也是占用4个字节的长度,因此整个结构体占用的内存大小为12个字节
⑸ linux内存分配默认是多少字节对齐
VC和GCC默认的都是4字节对齐,编程中可以使用#pragma pack(n)指定对齐模数。出现以上差异的原因在于,VC和GCC中对于double类型的对齐方式不同。
Win32平台下的微软VC编译器在默认情况下采用如下的对齐规则: 任何基本数据类型T的对齐模数就是T的大小,即sizeof(T)。比如对于double类型(8字节),就要求该类型数据的地址总是8的倍数,而char类型数据(1字节)则可以从任何一个地址开始。
Linux下的GCC奉行的是另外一套规则:任何2字节大小(包括单字节吗?)的数据类型(比如short)的对齐模数是2,而其它所有超过2字节的数据类型(比如long,double)都以4为对齐模数。
复杂类型(如结构)的默认对齐方式是它最长的成员的对齐方式,这样在成员是复杂类型时,可以最小化长度。
struct{char a;double b;}
在VC中,因为结构中存在double和char,按照最长数据类型对齐,char只占1B,但是加上后面的double所占空间超过8B,所以char独占8B;而double占8B,一共16Byte。
在GCC中,double长度超过4字节,按照4字节对齐,原理同上,不过char占4字节,double占两个4字节,一共12Byte。
⑹ 汇编高手来说下内存对齐背后CPU究竟是怎么操作的
内存对齐:
我们知道现代计算机体系中CPU按照双字、字、字节访问存储内存,并通过总线进行传输,若未经一定规则的对齐,CPU的访址操作与总线的传输操作将会异常的复杂,所以现代编译器中都会对内存进行自动的对齐。
1.内存对齐系数
说道内存对齐,就不得不说内存对齐系数, 对齐系数最简单的设置方法是使用 #pragma pack(n)进行设置,这部分点进链接在我的文章内有详细说明!
2.sizeof
说到内存对齐第二个不得不说的就是sizeof,它的基本作用是判断数据类型或者表达式长度,要注意的是这不是一个函数,而是一个C++中的关键字!字节数的计算在程序编译时进行,而不是在程序执行的过程中才计算出来!
3.类型的长度与数据成员对齐
你的计算机中,数据类型的长度指的就是在你的计算机中对数据类型使用sizeof得到的结果,当然这个在各种不同的编译环境下得到的结果是不同的。
比如在32位Visual Studio环境下:
cout << sizeof(char) << endl; // 1
cout << sizeof(short) << endl; // 2
cout << sizeof(int) << endl; // 4
cout << sizeof(long) << endl; // 4
cout << sizeof(double) << endl; // 8
而在64位G++编译环境下:
cout << sizeof(char) << endl; // 1
cout << sizeof(short) << endl; // 2
cout << sizeof(int) << endl; // 4
cout << sizeof(long) << endl; // 8
cout << sizeof(double) << endl; // 8
下面我将在32位Visual Studio环境下讲解数据成员对齐:
首先我们要清楚结构体struct中的成员在内存中的分配是连续的,struct内的首地址也就是struct内第一个数据成员的地址,换句话说struct内第一个数据成员离struct开始的距离offset = 0。
数据成员对齐的规则就是,而在第一个成员之后,每个成员距离struct首地址的距离 offset, 都是struct内成员自身长度(sizeof) 与 #pragma pack(n)中的n的最小值的整数倍,如果未经对齐时不满足这个规则,在对齐时就会在这个成员前填充空子节以使其达到数据成员对齐。
默认n为8时:
struct {
char a;
double b;
} myStruct;
cout << sizeof myStruct << endl; // 16
cout << (int *)&myStruct.a << endl; // 0024F898
cout << &myStruct.b << endl; // 0024F8A0(因运行时而异)
当设置n为4也就是min(sizeof(double), n) = 4 时:
#pragma pack(4)
struct {
char a;
double b;
} myStruct;
cout << sizeof myStruct << endl; // 12
cout << (int *)&myStruct.a << endl; // 0046F76C
cout << &myStruct.b << endl; // 0046F770
第一个例子时,最小值为8,填充7个字节到char a 之后。
第二个例子时,最小值为4,填充3个字节到char a之后。
4.整体对齐
编译器在进行过数据成员对齐之后,还要进行整体对齐。与数据对齐相似但不是完全相同, 如果数据对齐完成时struct的大小不是 struct内成员自身长度最大值(sizeof) 与 #pragma pack(n)中的n的最小值的整数倍。(注意这里是成员中长度最大的那个与n比较,而不是特定的一个成员。)就要在struct的最后添加空字节直到对齐。
当设置n为4也就是min(sizeof(short), n) = 2 时:
#pragma pack(4)
struct {
char a;
short b;
char c;
} myStruct;
cout << sizeof myStruct << endl; // 6
cout << (int *)&myStruct.a << endl; // 003DFED0
cout << &myStruct.b << endl; // 003DFED2
cout << (int *)&myStruct.c << endl; // 003DFED4
在上面的例子中,char a offset为0 因成员对齐占据[D0]填充[D1]共两个字节,short b是最大长度成员无需对齐占据[D2-D3]两个字节,它的offset是2,而char c的offset是4占据[D4]无需成员对齐,但此时struct的大小是2+2+1 = 5字节,不是2的整数倍,所以我们要填充空子节在最后直到struct大小达到2的整数倍,这就是整体对齐。
经过了数据成员对齐与整体对齐之后内存对齐就完成了,如果深入思考上述规则还会发现:即使是同样数目与数量的数据成员,在摆放的顺序不同时struct的大小也会不同,下面就是一个例子:
这样摆放是12字节:
却变成了8字节.png
由于这种特性,如果在网络编程或相关内存操作时如果不加以注意的话,就会造成隐秘而难以纠正的错误,请大家务必小心!