欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

深入內(nèi)存對齊的詳解

 更新時(shí)間:2013年05月13日 09:46:15   作者:  
本篇文章是對內(nèi)存對齊進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下

1.引子

    在結(jié)構(gòu)中,編譯器為結(jié)構(gòu)的每個(gè)成員按其自身的自然對界(alignment)條件分配空間。各個(gè)成員按照它們被聲明的順序在內(nèi)存中順序存儲(chǔ),第一個(gè)成員的地址和整個(gè)結(jié)構(gòu)的地址相同。

    例如,下面的結(jié)構(gòu)各成員空間分配情況(假設(shè)對齊方式大于2字節(jié),即#pragma pack(n), n = 2,4,8...下文將討論#pragmapack()):

復(fù)制代碼 代碼如下:

struct test
{
     char x1;
     short x2;
     float x3;
     char x4;
};

    結(jié)構(gòu)的第一個(gè)成員x1,其偏移地址為0,占據(jù)了第1個(gè)字節(jié)。第二個(gè)成員x2為short類型,其起始地址必須2字節(jié)對界,即偏移地址是2的倍數(shù)。因此,編譯器在x2和x1之間填充了一個(gè)空字節(jié),將x2放在了偏移地址為2的位置。結(jié)構(gòu)的第三個(gè)成員x3和第四個(gè)成員x4恰好落在其自然對界地址上,在它們前面不需要額外的填充字節(jié)。在test結(jié)構(gòu)中,成員x3要求4字節(jié)對界,是該結(jié)構(gòu)所有成員中要求的最大對界單元,因而test結(jié)構(gòu)的自然對界條件為4字節(jié),整個(gè)結(jié)構(gòu)體的大小是最大對界單元大小的整數(shù)倍(結(jié)構(gòu)體內(nèi)部有結(jié)構(gòu)體時(shí)也遵循這個(gè)規(guī)則,下文將提到),編譯器在成員x4后面填充了3個(gè)空字節(jié)。整個(gè)結(jié)構(gòu)所占據(jù)空間為12字節(jié)。
    關(guān)于為什么要內(nèi)存對齊,參考<解析內(nèi)存對齊 Data alignment: Straighten up and fly right的詳解>??戳诉@篇文章便可以更輕松的理解下面的內(nèi)容。

    好了,下面說說#pragma pack:

2.#pragma pack()

    該預(yù)處理指令用來改變對齊參數(shù)。在缺省情況下,C編譯器為每一個(gè)變量或數(shù)據(jù)單元按其自然對界條件分配空間。一般地,可以通過下面的方法來改變?nèi)笔〉膶R參數(shù):

     · 使用偽指令#pragma pack (n),C編譯器將按照n字節(jié)對齊。

     · 使用偽指令#pragma pack (),取消自定義字節(jié)對齊方式。

也可以寫成:

#pragma pack(push,n)

#pragma pack(pop)

#pragma pack (n)表示每個(gè)成員的對齊單元不大于n(n為2的整數(shù)次冪)。這里規(guī)定的是上界,只影響對齊單元大于n的成員,對于對齊字節(jié)不大于n的成員沒有影響。其實(shí)從字面意思,pack是“包裹,打包”的意思,#pragma pack(n)規(guī)定n個(gè)字節(jié)是一個(gè)“包裹”,個(gè)人認(rèn)為實(shí)在不理解的話可以認(rèn)為處理器一次性可以從內(nèi)存中讀/寫n個(gè)字節(jié),這樣好理解。對于大小小于n的成員,當(dāng)然是按照自己的對齊條件對齊,因?yàn)椴徽撛趺捶哦伎梢砸淮涡匀〕?。對于對齊條件大于n個(gè)字節(jié)的成員,成員按照自身的對齊條件對齊和按照n字節(jié)對齊需要相同的讀取次數(shù),但按照n字節(jié)對齊節(jié)省空間,何樂而不為呢。可以參考我上面提到的<解析內(nèi)存對齊 Data alignment: Straighten up and fly right的詳解>。下面是一位大牛的觀點(diǎn),和我說的是一個(gè)意思:

   All it means is that each member of it will require alignment no greater than n.It doesn't mean that each member will have alignment requirement n.Notice, after all, it's called pack and not align for a reason-- precisely because it controls packing, not alignment.

另外,GNU C還有如下的一種方式:

     · __attribute__((aligned (n))),讓所作用的結(jié)構(gòu)成員對齊在n字節(jié)自然邊界上。如果結(jié)構(gòu)中有成員的長度大于n,則按照最大成員的長度來對齊。

     · __attribute__ ((packed)),取消結(jié)構(gòu)在編譯過程中的優(yōu)化對齊,按照實(shí)際占用字節(jié)數(shù)進(jìn)行對齊。

以上的n = 1, 2, 4, 8, 16... 第一種方式較為常見。

3.結(jié)構(gòu)體內(nèi)成員如何找出自己的位置

首先遵循以下規(guī)則:

1.  每個(gè)成員分別取自己的對齊方式和#pragma pack指定的對齊參數(shù)二者的較小值作為自己的對齊方式。

2.  復(fù)雜類型(如結(jié)構(gòu))的對齊方式是該類型聲明時(shí)所使用的對齊方式,或者說是聲明時(shí)它的所有成員使用的對齊參數(shù)的最大值,最后和此時(shí)的#pragma pack指定的對齊參數(shù)二者取極小值。大牛是這么說的:

The documentation for #pragma pack(n) says that "The alignment of a member will be on a boundary that is either a multiple of n or a multiple of the size of the member,whichever is smaller". However I think this is incorrect; the docs should say that the alignment of a member will be on a boundary that is either a multiple of n or the alignment requirement of the member, whichever is smaller.

3.  對齊后的長度必須是成員中最大的對齊參數(shù)(不是成員的大?。?/STRONG>的整數(shù)倍,這樣在處理數(shù)組時(shí)可以保證每一項(xiàng)都邊界對齊。

4.  對于數(shù)組,比如:char a[3];這種,它的對齊方式和分別寫3個(gè)char是一樣的。也就是說它還是按1個(gè)字節(jié)對齊.

        如果寫: typedef char Array3[3];

        Array3這種類型的對齊方式還是按1個(gè)字節(jié)對齊,而不是按它的長度。

5.  不論類型是什么,對齊的邊界一定是1,2,4,8,16,32,64....中的一個(gè)。

看一個(gè)簡單的例子:

復(fù)制代碼 代碼如下:

#pragma pack(8)
struct s1
{
    short a;
    long b;
};
struct s2
{
    char c;
    s1 d;
    long long e;
};
#pragma pack()

    成員對齊有一個(gè)重要的條件:每個(gè)成員分別對齊。即每個(gè)成員按自己的方式對齊.

    也就是說上面雖然指定了按8字節(jié)對齊,但并不是所有的成員都是以8字節(jié)對齊。其對齊的規(guī)則是,每個(gè)成員按其類型的對齊參數(shù)(通常是這個(gè)類型的大小)和指定對齊參數(shù)(這里是8字節(jié))中較小的一個(gè)對齊。并且結(jié)構(gòu)的長度必須為所用過的所有對齊參數(shù)的整數(shù)倍(只要是最大的對齊參數(shù)的整數(shù)倍即可),不夠就補(bǔ)空字節(jié)(視編譯器而定)。

    S1中,成員a是2字節(jié)默認(rèn)按2字節(jié)對齊,指定對齊參數(shù)為8,這兩個(gè)值中取2,a按2字節(jié)對齊;成員b是4個(gè)字節(jié),默認(rèn)是按4字節(jié)對齊,這時(shí)就按4字節(jié)對齊,a后補(bǔ)2個(gè)字節(jié)后存放b,所以sizeof(S1)應(yīng)該為8。8是4的倍數(shù),滿足上述的第3條規(guī)則。

    S2中,c和S1中的a一樣,按2字節(jié)對齊,而d是個(gè)結(jié)構(gòu),它是8個(gè)字節(jié),它按什么對齊呢?對于結(jié)構(gòu)來說,它的默認(rèn)對齊方式就是該結(jié)構(gòu)定義(聲明)時(shí)它的所有成員使用的對齊參數(shù)中最大的一個(gè),S1的是4,小于指定的8。所以成員d就是按4字節(jié)對齊,c后補(bǔ)2個(gè)字節(jié),后面是8個(gè)字節(jié)的結(jié)構(gòu)體d。成員e是8個(gè)字節(jié),它是默認(rèn)按8字節(jié)對齊,和指定的一樣,所以它對到8字節(jié)的邊界上,這時(shí),已經(jīng)使用了12個(gè)字節(jié)了,所以d后又補(bǔ)上4個(gè)字節(jié),從第16個(gè)字節(jié)開始放置成員e。這時(shí),長度為24,已經(jīng)可以被最大對齊參數(shù)8(成員e按8字節(jié)對齊)整除。這樣,一共使用了24個(gè)字節(jié)。

   上面的不夠復(fù)雜?再來一個(gè):

復(fù)制代碼 代碼如下:

#pragma pack(4)
struct s1
{
    char a;
    double b;
};
#pragma pack()

#pragma pack(2)
struct s2
{
    char c;
    struct s1 st1;
};
#pragma pack()

 
#pragma pack(2)
struct s3
{
    char a;
    long b;
};
#pragma pack()

#pragma pack(4)
struct s4
{
    char c;
    struct s3 st3;
};
#pragma pack()

    先看s1,a放在偏移地址為0的位置(第一個(gè)字節(jié))。b默認(rèn)8字節(jié)對齊,但指定對齊參數(shù)是4字節(jié),所以b按4字節(jié)對齊,放在偏移地址為4的位置,a后補(bǔ)3個(gè)字節(jié)。所以sizeof(s1)是12。結(jié)構(gòu)體s1的對齊參數(shù)是4,下面會(huì)用到。

    再看s2,c放在第一個(gè)字節(jié)。st1自己的對齊參數(shù)是4,但此時(shí)指定的對齊參數(shù)是2,所以st1按照2字節(jié)對齊,c后補(bǔ)一個(gè)字節(jié)后存放st1。注意,st1內(nèi)部是不會(huì)變的,聲明s1時(shí)是什么樣就是什么樣,因?yàn)槲覀円WCsizeof(s2.st1) == sizeof(s1),如果不這樣就亂套了。這樣sizeof(s2)是14。結(jié)構(gòu)體s2的對齊參數(shù)是2,14是2的整數(shù)倍。

    再看s3,a放在第一個(gè)字節(jié)。b默認(rèn)4字節(jié)對齊,但指定的對齊參數(shù)是2,所以b按2字節(jié)對齊,放在偏移地址為2的位置,a后補(bǔ)一個(gè)字節(jié)。sizeof(s3)是6。結(jié)構(gòu)體s3的對齊參數(shù)是2(后面會(huì)用到),6是2的整數(shù)倍。

    最后看s4,c放在第一個(gè)字節(jié)。st3自己的對齊參數(shù)是2,指定的對齊參數(shù)是4,所以st3取極小值,按2字節(jié)對齊,放在偏移地址為2的位置,c后補(bǔ)一個(gè)字節(jié)。sizeof(s4)是8,結(jié)構(gòu)體的對齊參數(shù)是2,8是2的整數(shù)倍。

相關(guān)文章

  • C++遍歷文件夾下的所有文件

    C++遍歷文件夾下的所有文件

    數(shù)據(jù)分多個(gè)文件存儲(chǔ),讀取數(shù)據(jù)就需要對多個(gè)文件進(jìn)行操作。下面通過實(shí)例代碼給大家講解C++遍歷文件夾下的所有文件,感興趣的的朋友一起看看吧
    2017-08-08
  • C++ vector的用法小結(jié)

    C++ vector的用法小結(jié)

    這篇文章主要介紹了c++中,vector是一個(gè)十分有用的容器,下面對這個(gè)容器做一下總結(jié)
    2013-12-12
  • C語言數(shù)據(jù)結(jié)構(gòu)中堆排序的分析總結(jié)

    C語言數(shù)據(jù)結(jié)構(gòu)中堆排序的分析總結(jié)

    堆是計(jì)算機(jī)科學(xué)中一類特殊的數(shù)據(jù)結(jié)構(gòu)的統(tǒng)稱,通常是一個(gè)可以被看做一棵完全二叉樹的數(shù)組對象。而堆排序是利用堆這種數(shù)據(jù)結(jié)構(gòu)所設(shè)計(jì)的一種排序算法。本文將通過圖片詳細(xì)介紹堆排序,需要的可以參考一下
    2022-04-04
  • C語言數(shù)據(jù)結(jié)構(gòu)哈希表詳解

    C語言數(shù)據(jù)結(jié)構(gòu)哈希表詳解

    哈希表是一種根據(jù)關(guān)鍵碼去尋找值的數(shù)據(jù)映射結(jié)構(gòu),該結(jié)構(gòu)通過把關(guān)鍵碼映射的位置去尋找存放值的地方,說起來可能感覺有點(diǎn)復(fù)雜,我想我舉個(gè)例子你就會(huì)明白了,最典型的的例子就是字典
    2022-02-02
  • C++可擴(kuò)展性與多線程超詳細(xì)精講

    C++可擴(kuò)展性與多線程超詳細(xì)精講

    這篇文章主要介紹了C++可擴(kuò)展性與多線程,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧
    2022-10-10
  • C語言設(shè)計(jì)實(shí)現(xiàn)掃描器的自動(dòng)機(jī)的示例詳解

    C語言設(shè)計(jì)實(shí)現(xiàn)掃描器的自動(dòng)機(jī)的示例詳解

    這篇文章主要為大家詳細(xì)介紹了如何利用C語言設(shè)計(jì)實(shí)現(xiàn)掃描器的自動(dòng)機(jī),可識別的單詞包括:關(guān)鍵字、界符、標(biāo)識符和常整型數(shù),感興趣的小伙伴可以了解一下
    2022-12-12
  • 數(shù)據(jù)結(jié)構(gòu)之堆的具體使用

    數(shù)據(jù)結(jié)構(gòu)之堆的具體使用

    本文主要介紹了數(shù)據(jù)結(jié)構(gòu)之堆的具體使用,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-02-02
  • C++數(shù)據(jù)結(jié)構(gòu)模板進(jìn)階的多方面分析

    C++數(shù)據(jù)結(jié)構(gòu)模板進(jìn)階的多方面分析

    今天我要給大家介紹C++中的模板更深的一些知識。有關(guān)于非類型的模板參數(shù)和模板特化的一些知識,感興趣的朋友快來看看吧
    2022-02-02
  • 詳解C++設(shè)計(jì)模式編程中責(zé)任鏈模式的應(yīng)用

    詳解C++設(shè)計(jì)模式編程中責(zé)任鏈模式的應(yīng)用

    這篇文章主要介紹了C++設(shè)計(jì)模式編程中責(zé)任鏈模式的應(yīng)用,責(zé)任鏈模式使多個(gè)對象都有機(jī)會(huì)處理請求,從而避免請求的發(fā)送者和接收者之間的耦合關(guān)系,需要的朋友可以參考下
    2016-03-03
  • C++中delete和delete[]的區(qū)別詳細(xì)介紹

    C++中delete和delete[]的區(qū)別詳細(xì)介紹

    一直對C++中的delete和delete[]的區(qū)別不甚了解,今天遇到了,上網(wǎng)查了一下,得出了結(jié)論,拿出來和大家分享一下
    2012-11-11

最新評論