C/C++|关于linux下内存对齐的一些探究

1 基本数据类型在不同的平台上的占用内存 不同的平台上对不同数据类型分配的字节数是不同的。
个人对平台的理解是CPU+OS+Compiler,平台的概念是三者的组合。这三者都有32位、64位之分。但通常三者是匹配的,即64位的CPU+64位的OS+64位的Comliler。
实际上数据类型实际占用的字节数是由编译器决定的!
常用数据类型对应字节数。
可用如sizeof(char),sizeof(char*)等得出。
32位编译器:
char :1个字节
char*(即指针变量,只与地址寻址范围有关): 4个字节(32位的寻址空间是2^32, 即32个bit,也就是4个字节。同理64位编译器)
short int : 2个字节
int: 4个字节
unsigned int : 4个字节
float: 4个字节
double: 8个字节
long: 4个字节
long long: 8个字节
unsigned long: 4个字节
64位编译器:
char :1个字节
指针变量: 8个字节!!!
short int : 2个字节
int: 4个字节
unsigned int : 4个字节
float: 4个字节
double: 8个字节
long: 8个字节
long long: 8个字节
unsigned long: 8个字节
2 关于内存对齐 首先需要注意的是,内存对齐的规则在不同的编译环境下得到的结果是完全不一样的。网上关于这方面的一些讨论起码在我的机器(linux–gcc version 7.5.0)上是得不到的正确的验证结果的。所以下面是只针对linux-gcc环境下的。
2.1内存对齐的规则 如果程序中显式的说明了内存对齐的字节数(#pragma pack(n),n为1,2,4,8,16),那么内存对齐使用下面的规则:

  • 1、 对于结构的各个成员,第一个成员位于偏移为0的位置,以后每个数据成员的偏移量必须是min(#pragma pack()指定的数,这个数据成员的自身长度) 的倍数。
  • 2、 在数据成员完成各自对齐之后,结构(或联合)本身也要进行对齐,对齐将按照#pragma pack指定的数值和结构(或联合)最大数据成员长度中,比较小的那个进行。
(据了解,在其他的编译环境下,第二条是不成立的。)
下面是几个例子:
#include#pragma pack(4)using namespace std; class A { public: char c1; double d; //大小为8字节 int i; char c2; }; int main() { A cla; cout<<"size of A is"<

内存分布如下图所示:
C/C++|关于linux下内存对齐的一些探究
文章图片

若将上面的#pragma pack(4)改为#pragma pack(8),那么得到的结果将是24。这种情况下的内存分布是:
C/C++|关于linux下内存对齐的一些探究
文章图片

2.2 linux默认的内存对齐规则 【C/C++|关于linux下内存对齐的一些探究】搜集了一些资料后,比较认同下面这篇文章的观点: link.
这篇文章的观点是,在Linux gcc 下是没有默认对齐数的,默认下一次对齐到每个类型的对齐数处。
我总结的在linux下的对齐规则:
  • 1、 对于结构的各个成员,第一个成员位于偏移为0的位置,以后每个数据成员的偏移量必须是这个数据成员的自身长度)的倍数。
  • 2、 在数据成员完成各自对齐之后,结构(或联合)本身也要进行对齐,对齐将按照最大数据成员长度进行,也就是说,最后结构的大小是最大数据成员大小的整数倍
下面是例子:
#include //#pragma pack(8)struct test_plus { char a; long double b; char c; }; int main(void) { std::cout << sizeof(long double) << std::endl; //Long double的大小是16 std::cout << sizeof(test_plus) << std::endl; return 0; } //#pragma pack()

最后输出的结果是48,如果去掉#pragma pack(8)的注释,结果将是32。
2.3 结论 最后引用上述文章中的结论:
  • 首先肯定的是 linux gcc 没有默认对齐数。
  • 如果他们设置了默认对齐数,可能会对其他编译器产生不兼容的问题。
  • 其他的系统要求的对齐可能不相同,所以他们在 mingw-gcc on windows 做了一些改变。所以不同编译环境对齐规则是不一样的。
3 关于union的字节对齐 在存储多个成员信息时,编译器会自动给struct第个成员分配存储空间,struct 可以存储多个成员信息,而union每个成员会用同一个存储空间,只能存储最后一个成员的信息。
union实际占用的空间,是他的所有成员中,占用空间最大的一个成员的大小,再加上一定的字节对齐!!
union的对齐规则是和上面所说的是一样,也就是说:
1)如果程序中显式的说明了内存对齐的字节数(#pragma pack(n),n为1,2,4,8,16),那么内存对齐使用下面的规则:
  • 1、 首先占用的空间是union中最大的那个成员的大小
  • 2、 占用完初始空间后,union进行自身对齐,对齐规则是,对齐将按照#pragma pack指定的数值和结构(或联合)最大数据成员长度中,比较小的那个进行
2)在Linux gcc 下是没有默认对齐数的
  • 1 首先占用的空间是union中最大的那个成员的大小。
  • 2 占用完初始的空间后,union进行自身对齐,对齐规则是,对齐将按照最大数据成员长度进行,也就是说,最后结构的大小是最大数据成员大小的整数倍
下面是例子:
#include using namespace std; union u { double a; int b; }; union u2 { char a[11]; double b; }; union u3 { char a[13]; char b; }; int main() { cout<

也就是说,默认情况下,也就是上面说的第二种情况,根据union中最大的数据成员的大小进行对齐,也就是说,最后的大小是union成员中最大大小的整数倍。。
如果使用了#pragma pack(4),那么得到的u2的结果将会是12!!!这是因为根据#pragma pack指定的数值和联合最大数据成员长度中,比较小的那个进行。最后的结果是4的整数倍!!

    推荐阅读