关于c++:sizeof与内存对齐总结

33次阅读

共计 2248 个字符,预计需要花费 6 分钟才能阅读完成。

1. 根本类型

sizeof 运算符返回类型或者数据对象的长度 (字节),

  int a=1;
   float b=1.0;
   double c=1.0;
   long d=1;
   char e = '1'; 

    cout<<sizeof(a)<<""<<sizeof(b)<<" "<<sizeof(c)<<" "<<sizeof(d)<<" "<<sizeof(e)<<endl;
    cout<<sizeof(int)<<""<<sizeof(float)<<" "<<sizeof(double)<<" "<<sizeof(long)<<" "<<sizeof(char)<<endl;

输入:

4 4 8 8 1
4 4 8 8 1

2. 动态数组

将 sizeof 运算符用于动态数组名,失去的是整个数组的字节数,用于数组元素,则失去的是元素长度。

int arr[3]={1,1,1};
 float arr1[3]={1.0,1.0,1.0};
 double arr2[3]={1.0,1.0,1.0};
 cout<<sizeof(arr)<<""<<sizeof(arr1)<<" "<<sizeof(arr2)<<endl;
 cout<<sizeof(arr[0])<<""<<sizeof(arr1[1])<<" "<<sizeof(arr2[2])<<endl;

输入:

12 12 24
4 4 8

3. 动静数组

然而当 sizeof 运算符用于动静数组,就很神奇。

 vector<int>arr1(1,0);
  vector<int>arr2(2,0);
  vector<int>arr3(3,0);
   cout<<sizeof(arr1)<<""<<sizeof(arr2)<<" "<<sizeof(arr3)<<endl;

输入:

 24 24 24

能够看到不论 vector 有多少元素,输出都是 24,为啥?

这是因为 vector 是 C ++ 规范库中的容器类,其外部实现了三个指针,

  • start;
  • finish;
  • end_of_storage;

别离代表头,尾 (理论应用),vector 存储尾部 (占用的,通常大于理论应用),finish-start 对应于 size(),end_of_storage-start 对应于 capacity(),如下图所示:

vector 通过配置比其所包容的元素所需更多的内存,即先预留足够空间,防止二次调配,从而进步 vector 的性能。

so,sizeof(vec) 其实失去的是三个指针占用内存。在 64bit 零碎中,一个指针占用8个字节。

4. 构造体

构造体成员是依照定义时的程序顺次存储在间断的内存空间,然而构造体大小并不是所有成员大小之和。这波及字节对齐的问题。

4.1 偏移量

偏移量指的是构造体成员地址与构造体地址的差值。构造体大小等于最初一个成员的偏移量加上最初一个成员的大小。显然,构造体变量中第一个成员的地址就是构造体变量的首地址。

4.2 字节对齐

字节是计算机内存的根本单位,然而并不是一一字节存取,而是一次性存取 4 字节 (32 位零碎),8 个字节 (64 位零碎),这样子也对拜访地址做了限度,它必须是 4,8 的倍数。

如果数据地址不是 4,8 倍数会怎么样?

思考 64 位零碎,若没内存对齐规定,如果你的数据地址在 0x0001-0x0008,因为地址不是 0x0000 结尾,所以只能别离在 0x0000-0x0007 和 0x0008-0x0015 去两次数据,而后剔除多余数据,再把后果返回给你,这样子使得 IO 变慢。因为计算机 CPU 速度远远快于内存读写速度,所以缩小内存拜访次数是晋升执行速度的要害。

依照对齐规定存取数据,将数据放在 0x0000-0x0008,则可一次性取出,缩小拜访次数,晋升性能。

以下援用于 C /C++ 内存对齐详解

每个特定平台上的编译器都有本人的默认“对齐系数”(也叫对齐模数)。gcc 中默认 #pragma pack(4),能够通过预编译命令 #pragma pack(n),n = 1,2,4,8,16 来扭转这一系数。

无效对其值:是给定值 #pragma pack(n) 和构造体中最长数据类型长度中较小的那个。无效对齐值也叫对齐单位。

字节对齐规定如下

1. 第一个构造体成员的地址偏移量为 0,当前每个成员偏移量都是成员大小或者无效对齐字节数的最小值的整数倍。如有须要编译器会在成员之间加上填充字节。

2. 构造体总大小是无效对齐值的整数倍。如有须要编译器会在最末一个成员之后加上填充字节。

举几个例子

# 例 1
struct stru 
{  
int a;  
char b;  
int c;  
}

例 1,a 偏移量为 0,占用地址空间为 0 -3,而后 b 偏移量为 4(成员的大小 < 无效对齐字节),占用地址空间为 4,c 偏移量不为 5,而是 8,占用地址空间为 8 -11,占用了 12 个字节,又是无效对齐值的整数倍。

# 例 2
struct stru 
{  
char a;  
int b;
int c;  
}

例 2,a 偏移量为 0,占用地址空间为 0,而后 b 的偏移量也不能是 1,而是 4,b 占用的地址空间为 4 -7,c 偏移量为 8,占用地址空间为 8 -11,占用了 12 个字节。

# 例 3
struct stru 
{  
int a;  
char b;  
char c;  
}

例 3,a 占用 4 个字节,地址为 0,b 偏移量为 4(成员大小 < 无效对齐字节),占用地址空间为 4,c 偏移量为 5(成员大小 < 无效对齐字节),所以共 5 个字节,然而须要填充 3 个字节,所以例 3 构造大小为 8。

# 例 4
struct stru 
{  
char a;
int b;  
char c;  
}

例 4,a 的偏移量为 0,占用地址空间为 0,然而 b 的偏移量不能是 2 了,应该是 4,占用地址空间为 4 -7,而后 c 偏移为 8,共占用了 9 个字节,然而整个构造体成员大小为 12。

正文完
 0