久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

C/C++中的對齊

 changqiong0606 2012-08-07

http://www.cnblogs.com/cbscan/articles/2033138.html

對于內(nèi)存要求高的程序,,要有效控制數(shù)據(jù)結(jié)構(gòu)的大小,,熟悉對齊規(guī)則是必要的。在開始寫一個(gè)結(jié)構(gòu)體之前,,按照長度大小順序來寫成員是一個(gè)需要記住的技巧,。

許多實(shí)際的計(jì)算機(jī)系統(tǒng)對基本類型數(shù)據(jù)在內(nèi)存中存放的位置有限制,它們會要求這些數(shù)據(jù)的首地址的值是某個(gè)數(shù)k(通常它為4或8)的倍數(shù),,這就是所謂的內(nèi)存對齊,,而這個(gè)k則被稱為該數(shù)據(jù)類型的對齊模數(shù)(alignment modulus)。當(dāng)一種類型S的對齊模數(shù)與另一種類型T的對齊模數(shù)的比值是大于1的整數(shù),我們就稱類型S的對齊要求比T強(qiáng)(嚴(yán)格),,而稱T比S弱(寬松),。這種強(qiáng)制的要求一來簡化了處理器與內(nèi)存之間傳輸系統(tǒng)的設(shè)計(jì),二來可以提升讀取數(shù)據(jù)的速度,。比如這么一種處理器,,它每次讀寫內(nèi)存的時(shí)候都從某個(gè)8倍數(shù)的地址開始,一次讀出或?qū)懭?個(gè)字節(jié)的數(shù)據(jù),,假如軟件能保證double類型的數(shù)據(jù)都從8倍數(shù)地址開始,,那么讀或?qū)懸粋€(gè)double類型數(shù)據(jù)就只需要一次內(nèi)存操作。否則,,我們就可能需要兩次內(nèi)存操作才能完成這個(gè)動作,,因?yàn)閿?shù)據(jù)或許恰好橫跨在兩個(gè)符合對齊要求的8字節(jié)內(nèi)存塊上。某些處理器在數(shù)據(jù)不滿足對齊要求的情況下可能會出錯(cuò),,但是Intel的IA32架構(gòu)的處理器則不管數(shù)據(jù)是否對齊都能正確工作,。不過Intel奉勸大家,如果想提升性能,,那么所有的程序數(shù)據(jù)都應(yīng)該盡可能地對齊,。

ANSI C標(biāo)準(zhǔn)中并沒有規(guī)定,相鄰聲明的變量在內(nèi)存中一定要相鄰,。為了程序的高效性,,內(nèi)存對齊問題由編譯器自行靈活處理,這樣導(dǎo)致相鄰的變量之間可能會有一些填充字節(jié),。對于基本數(shù)據(jù)類型(int char),,他們占用的內(nèi)存空間在一個(gè)確定硬件系統(tǒng)下有個(gè)確定的值,所以,,接下來我們只是考慮結(jié)構(gòu)體成員內(nèi)存分配情況,。

struct T

{

  char ch;

  int   i   ;

};

Win32平臺下的微軟C編譯器(cl.exe for 80×86)的對齊策略:

1) 結(jié)構(gòu)體變量的首地址能夠被其最寬基本類型成員的大小所整除;

備注:編譯器在給結(jié)構(gòu)體開辟空間時(shí),,首先找到結(jié)構(gòu)體中最寬的基本數(shù)據(jù)類型,,然后尋找內(nèi)存地址能被該基本數(shù)據(jù)類型所整除的位置,作為結(jié)構(gòu)體的首地址,。將這個(gè)最寬的基本數(shù)據(jù)類型的大小作為上面介紹的對齊模數(shù),。

2) 結(jié)構(gòu)體每個(gè)成員相對于結(jié)構(gòu)體首地址的偏移量(offset)都是成員大小的整數(shù)倍,如有需要編譯器會在成員之間加上填充字節(jié)(internal adding),;

備注:為結(jié)構(gòu)體的一個(gè)成員開辟空間之前,,編譯器首先檢查預(yù)開辟空間的首地址相對于結(jié)構(gòu)體首地址的偏移是否是本成員的整數(shù)倍,若是,,則存放本成員,,反之,,則在本成員和上一個(gè)成員之間填充一定的字節(jié),以達(dá)到整數(shù)倍的要求,,也就是將預(yù)開辟空間的首地址后移幾個(gè)字節(jié),。

3) 結(jié)構(gòu)體的總大小為結(jié)構(gòu)體最寬基本類型成員大小的整數(shù)倍,如有需要,,編譯器會在最末一個(gè)成員之后加上填充字節(jié)(trailing padding),。

備注:結(jié)構(gòu)體總大小是包括填充字節(jié),最后一個(gè)成員滿足上面兩條以外,,還必須滿足第三條,,否則就必須在最后填充幾個(gè)字節(jié)以達(dá)到本條要求。

根據(jù)以上準(zhǔn)則,,在windows下,,使用VC編譯器,sizeof(T)的大小為8個(gè)字節(jié),。

而在GNU GCC編譯器中,,遵循的準(zhǔn)則有些區(qū)別,對齊模數(shù)不是像上面所述的那樣,,根據(jù)最寬的基本數(shù)據(jù)類型來定,。在GCC中,對齊模數(shù)的準(zhǔn)則是:對齊模數(shù)最大只能是4,,也就是說,,即使結(jié)構(gòu)體中有double類型,對齊模數(shù)還是4,,所以對齊模數(shù)只能是1,,2,4,。而且在上述的三條中,,第2條里,offset必須是成員大小的整數(shù)倍,,如果這個(gè)成員大小小于等于4則按照上述準(zhǔn)則進(jìn)行,,但是如果大于4了,則結(jié)構(gòu)體每個(gè)成員相對于結(jié)構(gòu)體首地址的偏移量(offset)只能按照是4的整數(shù)倍來進(jìn)行判斷是否添加填充,。最后的大小來按照4來判斷,。

看如下例子:struct T

{

  char ch;

  double   d   ;

};

那么在GCC下,,sizeof(T)應(yīng)該等于12個(gè)字節(jié),。

如果結(jié)構(gòu)體中含有位域(bit-field),那么VC中準(zhǔn)則又要有所更改:

1) 如果相鄰位域字段的類型相同,,且其位寬之和小于類型的sizeof大小,,則后面的字段將緊鄰前一個(gè)字段存儲,,直到不能容納為止;

2) 如果相鄰位域字段的類型相同,,但其位寬之和大于類型的sizeof大小,,則后面的字段將從新的存儲單元開始,其偏移量為其類型大小的整數(shù)倍,;

3) 如果相鄰的位域字段的類型不同,,則各編譯器的具體實(shí)現(xiàn)有差異,VC6采取不壓縮方式(不同位域字段存放在不同的位域類型字節(jié)中),,Dev-C++和GCC都采取壓縮方式,;

備注:當(dāng)兩字段類型不一樣的時(shí)候,對于不壓縮方式,,例如:struct N

{

  char c:2;

  int    i:4;

};

依然要滿足不含位域結(jié)構(gòu)體內(nèi)存對齊準(zhǔn)則第2條,,i成員相對于結(jié)構(gòu)體首地址的偏移應(yīng)該是4的整數(shù)倍,所以c成員后要填充3個(gè)字節(jié),,然后再開辟4個(gè)字節(jié)的空間作為int型,,其中4位用來存放i,所以上面結(jié)構(gòu)體在VC中所占空間為8個(gè)字節(jié),;而對于采用壓縮方式的編譯器來說,,遵循不含位域結(jié)構(gòu)體內(nèi)存對齊準(zhǔn)則第2條,不同的是,,如果填充的3個(gè)字節(jié)能容納后面成員的位,,則壓縮到填充字節(jié)中,不能容納,,則要單獨(dú)開辟空間,,所以上面結(jié)構(gòu)體N在GCC或者Dev-C++中所占空間應(yīng)該是4個(gè)字節(jié)。

4) 如果位域字段之間穿插著非位域字段,,則不進(jìn)行壓縮,;

備注:

結(jié)構(gòu)體typedef struct

{

   char c:2;

   double i;

   int c2:4;

}N3;

在GCC下占據(jù)的空間為16字節(jié),在VC下占據(jù)的空間應(yīng)該是24個(gè)字節(jié),。

ps:對齊模數(shù)的選擇只能是根據(jù)基本數(shù)據(jù)類型,,所以對于結(jié)構(gòu)體中嵌套結(jié)構(gòu)體,只能考慮其拆分的基本數(shù)據(jù)類型,。(而對于對齊準(zhǔn)則中的第2條,,確是要將整個(gè)結(jié)構(gòu)體看成是一個(gè)成員,成員大小按照該結(jié)構(gòu)體根據(jù)對齊準(zhǔn)則判斷所得的大小,。--不解這句話,?據(jù)程序結(jié)果來看,無論是成員對齊還是最后的總大小填充,,都是以最嚴(yán)格基本類型來判斷的)類對象在內(nèi)存中存放的方式和結(jié)構(gòu)體類似,,這里就不再說明,。需要指出的是,類對象的大小只是包括類中非靜態(tài)成員變量所占的空間,,如果有虛函數(shù),,那么再另外增加一個(gè)指針?biāo)嫉目臻g即可。

struct test

{

    char a;

    short b;

    double c;

};

struct test2

{

    char a;

    struct test t;

};

int main()

{

    struct test2 t;

    printf("%d\n", sizeof(t));

}

VC將輸出24,,而gcc將輸出16,。還有#pragma pack()的一點(diǎn)補(bǔ)充

自身對齊值:即數(shù)據(jù)類型的自身的對齊值。例如char型的數(shù)據(jù),,其自身對齊值為1字節(jié);short型的數(shù)據(jù),,其自身對齊值為2字節(jié);int,float,long類型,其自身對齊值為4字節(jié);double類型,,其自身對齊值為4字節(jié);而struct和class類型的數(shù)據(jù)其自身對齊值為其成員變量中自身對齊值最大的那個(gè)值,。

指定對齊值:#pragma pack (value)時(shí)指定的對齊值value

有效對齊值:上述兩個(gè)對齊值中最小的那個(gè)。

struct A {

char c;

int i;

short s;

};

#pragma pack (2) /* 指定按2字節(jié)對齊 */

struct B {

    char c;

    short s;

    int i;

};

#pragma pack () /* 恢復(fù)默認(rèn)對齊 */#pragma pack(push)和#pragma pack(pop)的意義是顯然的,。

#pragma pack(n)

至于編譯器的對齊選項(xiàng),,忽略不計(jì)。

為了防止不同編譯器對齊不一樣,,建議在代碼里面指定對齊參數(shù) 

可能重要的一點(diǎn)是關(guān)于緊縮結(jié)構(gòu)的,。緊縮結(jié)構(gòu)的用途 其實(shí)最常用的結(jié)構(gòu)對齊選項(xiàng)就是:默認(rèn)對齊和緊縮。在兩個(gè)程序,,或者兩個(gè)平臺之間傳遞數(shù)據(jù)時(shí),,我們通常會將數(shù)據(jù)結(jié)構(gòu)設(shè)置為緊縮的。這樣不僅可以減小通信量,,還可以避免對齊帶來的麻煩,。假設(shè)甲乙雙方進(jìn)行跨平臺通信,甲方使用了“/Zp2”這么奇怪的對齊選項(xiàng),,而乙方的編譯器不支持這種對齊方式,,那么乙方就可以理解什么叫欲哭無淚了。 當(dāng)我們需要一個(gè)字節(jié)一個(gè)字節(jié)訪問結(jié)構(gòu)數(shù)據(jù)時(shí),,我們通常都會希望結(jié)構(gòu)是緊縮的,,這樣就不必考慮哪個(gè)字節(jié)是填充字節(jié)了。我們把數(shù)據(jù)保存到非易失設(shè)備時(shí),,通常也會采用緊縮結(jié)構(gòu),,既減小存儲量,也方便其它程序讀出,。

各編譯器都支持結(jié)構(gòu)的緊縮,,即連續(xù)排列結(jié)構(gòu)的各成員變量,各成員變量之間沒有任何填充字節(jié)。這時(shí),,結(jié)構(gòu)的大小等于各成員變量大小的和。緊縮結(jié)構(gòu)的變量可以放在1n邊界,,即任意地址邊界,。在GNU gcc:

typedef struct St2Tag

{

    St1 st1;

    char ch2;

}

__attribute__ ((packed)) St2;

在ARMCC:

typedef __packed struct St2Tag

{

    St1 st1;

    char ch2;

} St2;

在VC:

#pragma pack(1)

typedef struct St2Tag

{

    St1 st1;

    char ch2;

} St2;

#pragma pack()

針對不同的編譯器:

#ifdef __GNUC__

#define GNUC_PACKED    __attribute__ ((packed))

#else

#define GNUC_PACKED

#endif

#ifdef __arm

#define ARM_PACKED    __packed

#else

#define ARM_PACKED

#endif

#ifdef WIN32

#pragma pack(1)

#endif

typedef ARM_PACKED struct St2Tag

{

    St1 st1;

    char ch2;

}

GNUC_PACKED St2;

#ifdef WIN32

#pragma pack()

#endif

最后記錄一個(gè)小細(xì)節(jié)。gcc編譯器和VC編譯器都支持在緊縮結(jié)構(gòu)中包含非緊縮結(jié)構(gòu),,例如前面例子中的St2可以包含非緊縮的St1,。但對于ARM編譯器而言,緊縮結(jié)構(gòu)包含的其它結(jié)構(gòu)必須是緊縮的,。如果緊縮的St2包含了非緊縮的St1,,編譯時(shí)就會報(bào)錯(cuò)。

    本站是提供個(gè)人知識管理的網(wǎng)絡(luò)存儲空間,,所有內(nèi)容均由用戶發(fā)布,,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式,、誘導(dǎo)購買等信息,,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,,請點(diǎn)擊一鍵舉報(bào),。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多