內(nèi)存對齊

內(nèi)存對齊

概念:

百度百科內(nèi)存對齊:編譯器為程序中的每個“數(shù)據(jù)單元”安排在適當(dāng)?shù)奈恢蒙?/p>

為什么要對齊?

一種提高內(nèi)存訪問速度的策略,cpu在訪問未對齊的內(nèi)存需要經(jīng)過兩次內(nèi)存訪問,而經(jīng)過內(nèi)存對齊一次就可以了.

舉例說明: 32bit的系統(tǒng)一次只能讀入32bit的數(shù)據(jù), 即4個字節(jié), 如果首地址為0, name讀入順序應(yīng)該是0-3, 3-7, 8-11 ...... 這樣的讀入; 在沒有對齊的情況下, 一個int變量(4字節(jié))可能分配中2,3,4,5這幾個位置上, 如果要獲取該變量, 就要進(jìn)行0-3, 4-7兩次讀取

像下面的struct:

typedef strutc test{
    char a;
    int b;
    char c;
} 
  • 未對齊
image

讀取b需要0-3, 4-7兩次讀取

  • 對齊
image

讀取b就可以從4-7一次讀取

對齊規(guī)則

1. 內(nèi)存的自然對齊

每一種數(shù)據(jù)類型都必須放在地址中的整數(shù)倍上

  • char(1)類型可以放在任何位置
  • short(2)只能放在0x2, 1x2, 2x2, 3x2.., 即0, 2, 4, 6 ...的位置上
  • int(4)只能放在0x4, 1x4, 2x4, 3x4.., 即0, 4, 8, 12 ...的位置上
  • double(8)只能放在0x8, 1x8, 2x8, 3x8.., 即0, 8, 16, 24 ...的位置上

note:以上規(guī)則針對的是#pragma pack(16)#pragma pack(8)時適用, 對其系數(shù)為1,2,4, 參見下邊的說明

代碼:

#import "ViewController.h"

typedef struct test{
    char a;
    int b;
    double c;
    char d;
}STU;
@interface ViewController ()
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    test();
}

int test(void)
{
    STU s;
    printf("s的大小是 = %d\n",(int)sizeof(STU));
    printf("s中a的起始地址是 %p\n",&(s.a));
    printf("s中b的起始地址是 %p\n",&(s.b));
    printf("s中c的起始地址是 %p\n",&(s.c));
    printf("s中d的起始地址是 %p\n",&(s.d));
    return 0;
}
@end

輸出:

s的大小是 = 24
s中a的起始地址是 0x7ffee06479c8
s中b的起始地址是 0x7ffee06479cc
s中c的起始地址是 0x7ffee06479d0
s中d的起始地址是 0x7ffee06479d8

如果按照規(guī)則一, 推斷出來的應(yīng)該是這樣的:

image

四字節(jié)對齊, 長度應(yīng)該是20位

結(jié)果其實是這樣的:

image

長度24位, 最后一個char補(bǔ)了7位, 原因就在于規(guī)則二

另外修改對齊系數(shù), 的出來的結(jié)果也不一樣:

pragma pack(16)
s的大小是 = 24
s中a的起始地址是 0x7ffee3e7f9c8
s中b的起始地址是 0x7ffee3e7f9cc
s中c的起始地址是 0x7ffee3e7f9d0
s中d的起始地址是 0x7ffee3e7f9d8
pragma pack(8)
大小是 = 24
s中a的起始地址是 0x7ffee2ab49c8
s中b的起始地址是 0x7ffee2ab49cc
s中c的起始地址是 0x7ffee2ab49d0
s中d的起始地址是 0x7ffee2ab49d8
pragma pack(4)
s的大小是 = 20
s中a的起始地址是 0x7ffee81239c8
s中b的起始地址是 0x7ffee81239cc
s中c的起始地址是 0x7ffee81239d0
s中d的起始地址是 0x7ffee81239d8
pragma pack(2)
s的大小是 = 16
s中a的起始地址是 0x7ffee03f09d0
s中b的起始地址是 0x7ffee03f09d2
s中c的起始地址是 0x7ffee03f09d6
s中d的起始地址是 0x7ffee03f09de

pragma pack(1)
s的大小是 = 14
s中a的起始地址是 0x7ffee2eb49d0
s中b的起始地址是 0x7ffee2eb49d1
s中c的起始地址是 0x7ffee2eb49d5
s中d的起始地址是 0x7ffee2eb49dd

2.補(bǔ)齊規(guī)則

在經(jīng)過第一原則分析后,檢查計算出的存儲單元是否為所有元素中最寬的元素的長度的整數(shù)倍,是,則結(jié)束;
若不是,則補(bǔ)齊為它的整數(shù)倍

因為代碼中:

typedef struct test{
    char a;
    int b;
    double c;
    char d;
}STU;

struct的成員類型double是8位長度, 所以struct的大小必須是8的倍數(shù), 所以最后一位char要補(bǔ)上7位

3. 包含結(jié)構(gòu)體成員的補(bǔ)齊規(guī)則

如果結(jié)構(gòu)體作為成員,則要找到這個結(jié)構(gòu)體中的最大元素,然后從這個最大成員的整數(shù)倍地址開始存儲

代碼:

typedef struct
{
    char a;
    int b;
    double c;
}X;

typedef struct {
    char a;
    X b;
}Y;

sizeof(X)為16,sizeof(Y)為24, 計算Y的存儲長度時,X的最大元素是double, 所以在存放第二個元素b時的初始位置是在double型的長度8的整數(shù)倍處,而非16的整數(shù)倍處,即系統(tǒng)為b所分配的存儲空間是第8~23個字節(jié)

另外:包含指針類型的情況。只要記住指針本身所占的存儲空間是8個字節(jié)(64bit系統(tǒng), 32bit下是4字節(jié))就行了,而不必看它是指向什么類型的指針

typedef struct
{
    int* b;
}X;

typedef struct {
    char *a;
}Y;

輸出:

X的大小是 = 8
Y的大小是 = 8

對齊規(guī)則(百科中的描述)

每個特定平臺上的編譯器都有自己的默認(rèn)“對齊系數(shù)”(也叫對齊模數(shù))。程序員可以通過預(yù)編譯命令#pragma pack(n),n=1,2,4,8,16來改變這一系數(shù),其中的n就是你要指定的“對齊系數(shù)”。

1、數(shù)據(jù)成員對齊規(guī)則:結(jié)構(gòu)(struct)(或聯(lián)合(union))的數(shù)據(jù)成員,第一個數(shù)據(jù)成員放在offset為0的地方,以后每個數(shù)據(jù)成員的對齊按照#pragma pack指定的數(shù)值和這個數(shù)據(jù)成員自身長度中,比較小的那個進(jìn)行。

2、結(jié)構(gòu)(或聯(lián)合)的整體對齊規(guī)則:在數(shù)據(jù)成員完成各自對齊之后,結(jié)構(gòu)(或聯(lián)合)本身也要進(jìn)行對齊,對齊將按照#pragma pack指定的數(shù)值和結(jié)構(gòu)(或聯(lián)合)最大數(shù)據(jù)成員長度中,比較小的那個進(jìn)行。

3、結(jié)合1、2可推斷:當(dāng)#pragma pack的n值等于或超過所有數(shù)據(jù)成員長度的時候,這個n值的大小將不產(chǎn)生任何效果。

32bit與64bit字節(jié)對比:

image

參考:

結(jié)構(gòu)體在內(nèi)存中的對其規(guī)則

內(nèi)存對齊與內(nèi)存分配原則

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 源網(wǎng)址[英文] github上有大神翻譯了一篇內(nèi)存對齊的英文文獻(xiàn),我復(fù)現(xiàn)了一下過程; 發(fā)現(xiàn)其中有個地方有出入(st...
    十曰立閱讀 1,224評論 0 3
  • C語言是面向過程的,而C++是面向?qū)ο蟮?C和C++的區(qū)別: C是一個結(jié)構(gòu)化語言,它的重點(diǎn)在于算法和數(shù)據(jù)結(jié)構(gòu)。C程...
    小辰帶你看世界閱讀 1,268評論 0 1
  • 首先通過一段代碼來描述內(nèi)存對齊的現(xiàn)象。 上述代碼打印出來的結(jié)果為:12,8 為什么相同的結(jié)構(gòu)體,只是交換了變量 a...
    xuyafei86閱讀 3,090評論 2 15
  • 內(nèi)存對齊: 我們知道現(xiàn)代計算機(jī)體系中CPU按照雙字、字、字節(jié)訪問存儲內(nèi)存,并通過總線進(jìn)行傳輸,若未經(jīng)一定規(guī)則的對齊...
    null122閱讀 9,600評論 12 40
  • 今天老師教了一個新的妝容-魅惑晚宴妝,明天要學(xué)習(xí)畫,心里有點(diǎn)緊張之前的妝容都沒有熟悉清楚又有新的來了。下午我給和我...
    青空書屋閱讀 285評論 0 0

友情鏈接更多精彩內(nèi)容