国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學(xué)院 > 開發(fā)設(shè)計(jì) > 正文

預(yù)處理指令#pragma pack詳解

2019-11-10 17:54:43
字體:
供稿:網(wǎng)友

預(yù)處理指令#PRagma pack詳解

#pragma pack的大致作用即為改變編譯器的對(duì)齊方式,先從指令和定義上來分析其功能。 部分內(nèi)容參考http://www.cnblogs.com/King-Gentleman/p/5297355.html 以及MSDN。

簡(jiǎn)單理解#pragma

作為較為復(fù)雜的預(yù)處理指令之一,它的作用為更改編譯器的編譯狀態(tài)以及為特定的編譯器提供特定的編譯指示,這些指示是具體針對(duì)某一種(或某一些)編譯器的,其他編譯器可能不知道該指示的含義又或者對(duì)該指示有不同的理解,也即是說,#pragma的實(shí)現(xiàn)是與具體平臺(tái)相關(guān)的。可以簡(jiǎn)單將其理解為該預(yù)處理指令是開發(fā)者和編譯器交互的一個(gè)工具。

#pragma pack指令說明

由于內(nèi)存的讀取時(shí)間遠(yuǎn)遠(yuǎn)小于CPU的存儲(chǔ)速度,這里用設(shè)定數(shù)據(jù)結(jié)構(gòu)的對(duì)齊系數(shù),即犧牲空間來換取時(shí)間的思想來提高CPU的存儲(chǔ)效率。

這里先說編譯器的對(duì)齊配置。以vc6為例,vc6中的編譯選項(xiàng)有 /Zp[1|2|4|8|16] ,/Zp1表示以1字節(jié)邊界對(duì)齊,相應(yīng)的,/Zpn表示以n字節(jié)邊界對(duì)齊。n字節(jié)邊界對(duì)齊的意思是說,一個(gè)成員的地址必須安排在成員的尺寸的整數(shù)倍地址上或者是n的整數(shù)倍地址上,取它們中的最小值。也就是: min ( sizeof ( member ), n) 實(shí)際上,1字節(jié)邊界對(duì)齊也就表示了結(jié)構(gòu)成員之間沒有空洞。 /Zpn選項(xiàng)是應(yīng)用于整個(gè)工程的,影響所有的參與編譯的結(jié)構(gòu)。 要使用這個(gè)選項(xiàng),可以在vc6中打開工程屬性頁,c/c++頁,選擇Code Generation分類,在Struct member alignment可以選擇。 而如果要專門針對(duì)某些結(jié)構(gòu)定義使用對(duì)齊選項(xiàng),可以使用#pragma pack編譯指令。指令語法如下: #pragma pack( [ show ] | [ push | pop ] [, identifier ] , n )

指令用法說明:

pack提供數(shù)據(jù)聲明級(jí)別的控制,對(duì)定義不起作用;調(diào)用pack時(shí)不指定參數(shù),n將被設(shè)成默認(rèn)值;一旦改變數(shù)據(jù)類型的alignment,直接效果就是占用memory的減少,但是performance會(huì)下降。

語法具體分析:

show:可選參數(shù);顯示當(dāng)前packing aligment的字節(jié)數(shù),以warning message的形式被顯示;push:可選參數(shù);將當(dāng)前指定的packing alignment數(shù)值進(jìn)行壓棧操作,這里的棧是the internal compiler stack,同時(shí)設(shè)置當(dāng)前的packing alignment為n;如果n沒有指定,則將當(dāng)前的packing alignment數(shù)值壓棧;pop:可選參數(shù);從internal compiler stack中刪除最頂端的record;如果沒有指定n,則當(dāng)前棧頂record即為新的packing alignment數(shù)值;如果指定了n,則n將成為新的packing aligment數(shù)值;如果指定了identifier,則internal compiler stack中的record都將被pop直到identifier被找到,然后pop出identitier,同時(shí)設(shè)置packing alignment數(shù)值為當(dāng)前棧頂?shù)膔ecord;如果指定的identifier并不存在于internal compiler stack,則pop操作被忽略;identifier:可選參數(shù);當(dāng)同push一起使用時(shí),賦予當(dāng)前被壓入棧中的record一個(gè)名稱;當(dāng)同pop一起使用時(shí),從internal compiler stack中pop出所有的record直到identifier被pop出,如果identifier沒有被找到,則忽略pop操作;n:可選參數(shù);指定packing的數(shù)值,以字節(jié)為單位;缺省數(shù)值是8,合法的數(shù)值分別是1、2、4、8、16。

先以#pragma pack(n)的使用舉例,該條指令功能與意義和/Zpn選項(xiàng)相同。#pragma pack(4)表示后續(xù)結(jié)構(gòu)單元的對(duì)齊系數(shù)為4(具體規(guī)則見下面說明),#pragma pack()表示更改當(dāng)前對(duì)齊系數(shù)為默認(rèn)值(未修改時(shí),工程對(duì)齊系數(shù)默認(rèn)為8)。

#pragma pack(4) struct test{ char m1; short int m2; int m3; };#pragma pack() test t; printf("%d/n", sizeof(t));

上述代碼段執(zhí)行結(jié)果為8,在初識(shí)時(shí)可能會(huì)誤作是12,為8的原因在于“結(jié)構(gòu)體中的數(shù)據(jù)成員,除了第一個(gè)是始終放在最開始的地方,其它數(shù)據(jù)成員的地址必須是它本身大小或?qū)R參數(shù)兩者中較小的一個(gè)的倍數(shù)。”

數(shù)據(jù)對(duì)齊規(guī)則

經(jīng)過上面這個(gè)例子,我們?cè)賮砜磳?duì)齊的規(guī)則。

復(fù)雜類型中各個(gè)成員按照它們被聲明的順序在內(nèi)存中順序存儲(chǔ),第一個(gè)成員的地址和整個(gè)類型的地址相同;每個(gè)成員分別對(duì)齊,即每個(gè)成員按自己的方式對(duì)齊,并最小化長(zhǎng)度;規(guī)則就是每個(gè)成員按其類型的對(duì)齊參數(shù)(通常是這個(gè)類型的大小)和指定對(duì)齊參數(shù)中較小的一個(gè)對(duì)齊;結(jié)構(gòu)體、聯(lián)合體或者類的數(shù)據(jù)成員,第一個(gè)放在偏移為0的地方;以后每個(gè)數(shù)據(jù)成員的對(duì)齊,按照#pragma pack指定的數(shù)值和這個(gè)數(shù)據(jù)成員自身長(zhǎng)度兩個(gè)中比較小的那個(gè)進(jìn)行;也就是說,當(dāng)#pragma pack指定的值等于或者超過所有數(shù)據(jù)成員長(zhǎng)度的時(shí)候,這個(gè)指定值的大小將不產(chǎn)生任何效果;復(fù)雜類型(如結(jié)構(gòu)體)整體的對(duì)齊是按照結(jié)構(gòu)體中長(zhǎng)度最大的數(shù)據(jù)成員和#pragma pack指定值之間較小的那個(gè)值進(jìn)行;這樣當(dāng)數(shù)據(jù)成員為復(fù)雜類型(如結(jié)構(gòu)體)時(shí),可以最小化長(zhǎng)度;復(fù)雜類型(如結(jié)構(gòu)體)整體長(zhǎng)度的計(jì)算必須取所用過的所有對(duì)齊參數(shù)的整數(shù)倍,不夠補(bǔ)空字節(jié);也就是取所用過的所有對(duì)齊參數(shù)中最大的那個(gè)值的整數(shù)倍,因?yàn)閷?duì)齊參數(shù)都是2的n次方;這樣在處理數(shù)組時(shí)可以保證每一項(xiàng)都邊界對(duì)齊。

另外,在相同的對(duì)齊方式下,結(jié)構(gòu)體內(nèi)部數(shù)據(jù)定義的順序不同,結(jié)構(gòu)體整體占據(jù)內(nèi)存空間也不同。舉例如下:

struct A {int a; // a的自身對(duì)齊值為4,偏移地址為0x00~0x03,a的起始地址0x00滿足0x00%4=0;char b; // b的自身對(duì)齊值為1,由于緊跟a之后的地址,即0x04滿足0x04%1=0,所以b存放在0x04地址空間short c; // c的自身對(duì)齊值為2,由于緊跟b之后的地址0x05%2不是0,而0x06%2=0,因此c的存放起始地址為0x06,存放在0x06~0x07空間。 // 在b和c之間的0x05地址 則補(bǔ)空字節(jié)。};

結(jié)構(gòu)體A中包含了4字節(jié)長(zhǎng)度的int一個(gè),1字節(jié)長(zhǎng)度的char一個(gè)和2字節(jié)長(zhǎng)度的short型數(shù)據(jù)一個(gè)。所以A用到的空間應(yīng)該是7字節(jié)。但是因?yàn)榫幾g器要對(duì)數(shù)據(jù)成員在空間上進(jìn)行對(duì)齊。而由于結(jié)構(gòu)體自身對(duì)齊值取數(shù)據(jù)成員中自身對(duì)齊值的最大值,即4,并且0x00~0x07的8字節(jié)空間滿足8%4=0,所以sizeof(strcut A)值為8。 現(xiàn)在把該結(jié)構(gòu)體調(diào)整成員變量的順序。

struct B {char b; // b的自身對(duì)齊值為1,其起始地址為0x00,由于滿足0x00%1=0,所以b存放在0x00地址空間int a; // a的自身對(duì)齊值為4,由于緊跟b之后的地址0x01%4不是0,而0x04%4=0,因此c的存放起始地址為0x04,存放在0x04~0x07空間。 // 在b和a之間的0x01~0x03地址則補(bǔ)3個(gè)空字節(jié)。short c; // c的自身對(duì)齊值為2,由于緊跟a之后的地址0x08%2=0,因此c的存放起始地址為0x08,存放在0x08~0x09空間。};

這時(shí)候同樣是總共7個(gè)字節(jié)的變量,但是由于結(jié)構(gòu)體自身對(duì)齊值取數(shù)據(jù)成員中自身對(duì)齊值的最大值,即4,并且0x00~0x09的10字節(jié)空間不滿足10%4=0,而12%4=0,所以sizeof(struct B)的值卻是12,即在緊跟c之后的0x0A~0x0B地址還需補(bǔ)兩個(gè)空字節(jié),使得整個(gè)結(jié)構(gòu)體占用的字節(jié)空間為12個(gè)字節(jié)。

現(xiàn)在我們使用預(yù)處理指令來指定結(jié)構(gòu)體的對(duì)齊系數(shù):

#pragma pack (2) /*指定按2字節(jié)對(duì)齊,等價(jià)于#pragma pack(push,2)*/struct C {char b;int a;short c;};#pragma pack () /*取消指定對(duì)齊,恢復(fù)缺省對(duì)齊,等價(jià)于#pragma pack(pop)*/

由上述規(guī)則4可知,結(jié)構(gòu)體的對(duì)齊是按照結(jié)構(gòu)體中長(zhǎng)度最大的數(shù)據(jù)成員和#pragma pack指定值之間較小的那個(gè)值進(jìn)行的,所以這里該值為2,sizeof(struct C)值是8。

這些例子引出了幾個(gè)具體的概念如下:

數(shù)據(jù)類型自身的對(duì)齊值:就是上面交代的基本數(shù)據(jù)類型的自身對(duì)齊值。指定對(duì)齊值:#pragma pack (value)時(shí)的指定對(duì)齊值value。結(jié)構(gòu)體或者類的自身對(duì)齊值:其數(shù)據(jù)成員中自身對(duì)齊值最大的那個(gè)值。數(shù)據(jù)成員、結(jié)構(gòu)體和類的有效對(duì)齊值:自身對(duì)齊值和指定對(duì)齊值中小的那個(gè)值。

其他參數(shù)使用舉例

#pragma pack(push, 1) // 將對(duì)齊系數(shù)1壓入internal compiler stack,同時(shí)設(shè)置當(dāng)前的packing alignment為1#pragma pack(push, 4) // 將4也壓入棧,同時(shí)設(shè)置當(dāng)前的packing alignment為4#pragma pack(show) // 顯示當(dāng)前packing alignment的字節(jié)數(shù)#pragma pack(pop) // 彈出internal compiler stack頂端的一條記錄,這里彈出了4#pragma pack(show) // 此時(shí)頂端的packing alignment值為1, struct test{ char m1; short m2; int m3; };#pragma pack() // 更改為缺省值8 #pragma pack(show)

在VS2013下的編譯結(jié)果如下圖所示。 編譯結(jié)果

另外值得一提的是,在ARM平臺(tái)的編譯器中,沒有提供如“#pragma pack”這樣帶參數(shù)的對(duì)齊指令,只有一個(gè)關(guān)鍵字 __packed。 __packed 限定符將所有有效類型的對(duì)齊邊界設(shè)置為 1,如果一個(gè)結(jié)構(gòu)沒有這個(gè)限定符,默認(rèn)向表數(shù)能力最強(qiáng)的那個(gè)數(shù)據(jù)類型對(duì)齊。

typedef __packed struct { double dValue1; char u8Value2; int u32Value3;} ASampleStructor;

上例中,size值為13,說明1字節(jié)對(duì)齊后,該結(jié)構(gòu)總長(zhǎng)為13字節(jié)。去掉__packed對(duì)齊后,為16字節(jié)。


發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 同德县| 乡城县| 青河县| 平定县| 阿巴嘎旗| 新宾| 合山市| 宜州市| 博兴县| 盐亭县| 沁源县| 潍坊市| 潮安县| 佛冈县| 横山县| 井研县| 双流县| 科尔| 莒南县| 德化县| 临夏县| 遂溪县| 平湖市| 榕江县| 宜兰市| 大庆市| 清远市| 太康县| 青神县| 怀集县| 顺昌县| 荔浦县| 龙山县| 叙永县| 怀宁县| 和田市| 仪征市| 老河口市| 彩票| 阳原县| 昆山市|