内存对齐 (C++)

内存对齐的概念

引入代码

众所周知,C++的空类占用 1 个字节的内存空间,非空类占用的空间与类内的成员有关。
但类中成员所占内存并不是连续的,拿以下代码举例:

#include<iostream>
using namespace std;
class test1
{
    char c1;
    int a;
    char c2;
}t1;

class test2
{
    char c1;
    char c2;
    int a;
}t2;

class test3
{
    int a;  
    char c1;
    char c2;
}t3;

int main()
{
    cout<<"size of t1="<<sizeof(t1)<<endl;
    cout<<"size of t2="<<sizeof(t2)<<endl;
    cout<<"size of t3="<<sizeof(t3)<<endl;
    system("pause");
 } 

在 32 位系统中,int 类型占用 4 字节,char 类型占用 1 字节,并且 3 个 test 类成员除顺序外完全一样,理论上输出结果均为 6
但实际 t1,t2,t3 的输出为 12 8 8,不仅大小与理论不符,t1 所占空间还要大于后两者。
这是因为成员变量的存储并不是连续的,而是根据一定的块大小存储 (一般默认为 4),这就是所谓的内存对齐

内存对齐的规则

对齐系数与有效对齐值

首先明确两个概念

  • 对齐系数:每个特定平台上的编译器都有自己的默认 「对齐系数」(也叫对齐模数)。linux 中默认为 4,vs 中默认为 8,可以通过预编译命令 #pragma pack(n),n = 1,2,4,8,16 来改变这一系数。

  • 有效对齐值:是给定值 #pragma pack(n) 和类中最长数据类型长度中较小的那个,也叫对齐单位。比如 vs 中默认对齐系数为 8,但该类中最长数据类型 int 为 4,则有效对齐值为 4。

为方便理解引入以下代码:

#include<iostream>
using namespace std;
class test1
{
    char c1;
    char c3;
    char c2;
}t1;

int main()
{
    cout<<"size of t1="<<sizeof(t1)<<endl;
    system("pause");
 } 

虽然默认对齐系数为 4,但该类中最长数据类型 char 为 1,所以有效对齐值为 1,结果输出为 3

内存对齐的具体规则为

  1. 第一个成员变量放在 offset 为 0 的地方,以后每个成员变量的对齐按照有效对齐值进行。
  2. 在成员变量完成各自对齐之后,类 (结构或联合) 本身也要进行对齐,对齐将按照有效对齐值进行。
  3. 类的总大小为最大对其数 (每一个成员变量都有一个对其数) 的整数倍。
  4. 如果存在类嵌套,则将嵌套类展开后对每一个成员变量对齐。

值得注意的是,n 的缺省数值是按照编译器自身设置,gcc 默认为 4,合法的数值分别是 1、2、4、8、16。
(即编译器只会按照 1、2、4、8、16 的方式分割内存,其他值无效)

字节边界 (重要!)

除了要考虑有效对齐值和对齐系数外,还需要引入字节边界的概念,具体规则为:
char 在 1 字节的边界上对齐,short 在 2 字节的边界上对齐,int、long 和 float 在 4 字节的边界上对齐,double 在 8 字节的边界上对齐,依次类推

这里引入一个例子:

class test4
{
    char c1;
    short c2;
    char c3;
    int c4;
}t4;

如果不引入字节边界的话,则 t4 理应占用 8 个字节(c1c2c3 共占 4 字节,c4 独占 4 字节),但实际上 t4 在内存中占用空间为 12 个字节,具体内存分配可看后续图示

图示

test 的内存分配如下

file

如果把使用 #pragma pack(n)把默认的对齐系数改为 1,代码如下

#include<iostream>
using namespace std;
#pragma pack(1)//设定为 1 字节对齐
class test1
{
    char c1;
    int a;
    char c2;
}t1;

class test2
{
    char c1;
    char c2;
    int a;
}t2;

class test3
{
    int a;
    char c1;
    char c2;
}t3;

class test4
{
    char c1;
    short c2;
    char c3;
    int c4;
}t4;

int main()
{
    cout << "size of t1=" << sizeof(t1) << endl;
    cout << "size of t2=" << sizeof(t2) << endl;
    cout << "size of t3=" << sizeof(t3) << endl;
    cout << "size of t4=" << sizeof(t4) << endl;
    system("pause");
}

则此时 t1,t2,t3 输出结果均为 6,t4 输出结果为 8

本文作者:小小黑
本文链接:https://lonelyenderman.top/archives/402
版权声明:本站采用 BY-NC-SA 进行许可。转载请注明出处!
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(*^▽^*)
 ̄﹃ ̄
(╯‵□′)╯︵┴─┴
(~ ̄▽ ̄)~
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
( ͡° ͜ʖ ͡°)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
つ﹏⊂
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
(´▽`ʃ♡ƪ)
w(゚Д゚)w
(๑•̀ㅂ•́)و✧
(#`O′)
凸 (艹皿艹 )
o(≧口≦)o
≡ω≡
(*/ω\*)
○| ̄|_
(⊙ˍ⊙)
Σ(っ °Д °;)っ
o( ̄ヘ ̄o#)
<( ̄︶ ̄)>
(。・∀・)ノ゙
(o゜▽゜)o☆
╥﹏╥
ヾ(´・ω・`)ノ
😂
😀
😅
😊
🙂
😍
😘
😜
😝
😏
😒
🙄
😳
😔
😫
😱
😭
😶
🌚
😣
🤨
😣
🤐
😪
🤤
🥵
🤮
😨
😱
😓
🤬
👴
🤡
🙈
💊
🙏
🤺
💩
👻
🙌
🖕
👍
👫
👌
🙏
👀
🐒
🔪
Source: github.com/zhheo/Sticker-Heo
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
Heo
花!
上一篇
下一篇