关于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<iostream>

#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"<<sizeof(A)<<endl;//输出20

    return 0 ;
}
//#pragma pack()

内存分布如下图所示:
在这里插入图片描述
若将上面的#pragma pack(4)改为#pragma pack(8),那么得到的结果将是24。这种情况下的内存分布是:
在这里插入图片描述

2.2 linux默认的内存对齐规则

搜集了一些资料后,比较认同下面这篇文章的观点: link.

这篇文章的观点是,在Linux gcc 下是没有默认对齐数的,默认下一次对齐到每个类型的对齐数处。

我总结的在linux下的对齐规则:

  • 1、 对于结构的各个成员,第一个成员位于偏移为0的位置,以后每个数据成员的偏移量必须是这个数据成员的自身长度)的倍数。

  • 2、 在数据成员完成各自对齐之后,结构(或联合)本身也要进行对齐,对齐将按照最大数据成员长度进行,也就是说,最后结构的大小是最大数据成员大小的整数倍

下面是例子:

#include <iostream>
//#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<iostream>
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<<sizeof(u)<<endl;//输出是8
    cout<<sizeof(u2)<<endl;//输出是16
    cout<<sizeof(u3)<<endl;//输出是13


    return 0;
}

也就是说,默认情况下,也就是上面说的第二种情况,根据union中最大的数据成员的大小进行对齐,也就是说,最后的大小是union成员中最大大小的整数倍。。

如果使用了#pragma pack(4),那么得到的u2的结果将会是12!!!这是因为根据#pragma pack指定的数值和联合最大数据成员长度中,比较小的那个进行。最后的结果是4的整数倍!!

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值