<dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><small id="yhprb"></small><dfn id="yhprb"></dfn><small id="yhprb"><delect id="yhprb"></delect></small><small id="yhprb"></small><small id="yhprb"></small> <delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"></dfn><dfn id="yhprb"></dfn><s id="yhprb"><noframes id="yhprb"><small id="yhprb"><dfn id="yhprb"></dfn></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><small id="yhprb"></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn> <small id="yhprb"></small><delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn>

新聞中心

字節那些事兒

作者: 時(shí)間:2016-07-29 來(lái)源:網(wǎng)絡(luò ) 收藏

  7、 如何控制對齊

本文引用地址:http://dyxdggzs.com/article/201607/294782.htm

  控制程序的對齊行為是一個(gè)與編譯器相關(guān)的工作。以下編譯指示( directive )被許多編譯器認可:

  #pragma pack(n)

  #pragma pack()

  任何處于這兩個(gè)編譯指示語(yǔ)句之間的數據結構,將采用 n 的數據對齊方式。 n 是一個(gè)可以指定的數字,取值范圍請參閱所使用編譯器的文檔,通常都會(huì )取值為 2 的冪?,F代編譯器在對程序進(jìn)行編譯時(shí),處于效率方面的考慮,會(huì )對數據結構的內存布局使用一個(gè)默認的字節對齊值,這個(gè)值一般都可以在命令行上顯式指定。如果要在一個(gè)頭文件 / 源文件中對特定的部分指定對齊屬性,則需要上述的編譯指示。結束指示的寫(xiě)法在某些編譯器或者平臺下需要寫(xiě)成:

  #pragma pack(pop)

  我們用一個(gè)例子來(lái)看一下這兩個(gè)指示的實(shí)際效用,看它究竟是如何影響數據的內存排列的。假定我們有如下的數據結構定義:

  struct S1

  {

  int i;

  char c;

  short s;

  };

  struct S2

  {

  char c;

  int i;

  short s;

  };

  這兩個(gè)結構的成員看起來(lái)是一樣的,只不過(guò)換了一下順序而已。我們使用 sizeof() 操作符來(lái)測量各自占用多少字節(除非特別指出,均在 32 位平臺上,并認為 int 占用 4 字節, char 占用 1 字節, short 占用 2 字節)。答案似乎不可思議, sizeof(S1) 的結果是 8 ,而 sizeof(S2) 卻是 12 。差異是怎么來(lái)的呢?原因就在于編譯器缺省的字節對齊設定在發(fā)生作用。

  這里需要引入以下概念和規則:

  概念及規則一,原生數據類(lèi)型自身對齊值。原生數據類(lèi)型即是 C/C++ 直接支持的數據類(lèi)型,也可以稱(chēng)為內建(built in )數據類(lèi)型。它們的自身對齊值分別為: char 為 1 , short int 為 2 , int 、 float 、 double 等為 4 ,不受符號位(即正負)的影響。

  概念及規則二,用戶(hù)數據類(lèi)型自身對齊值。用戶(hù)數據類(lèi)型即由程序員定義的類(lèi)、結構、聯(lián)合等,也叫抽象數據類(lèi)型( ADT )。它們的自身對齊值等同于為其成員的對齊值中的最大值。

  概念及規則三,用戶(hù)指定對齊值。程序員在編譯器命令行上的指定值,或者在 pragma pack 編譯指示中指定的值,對最終數據的影響取就近原則(顯然 pragma pack 指示會(huì )覆蓋命令行的指定)。

  概念及規則四,有效對齊值。取數據類(lèi)型的自身對齊值與用戶(hù)指定對齊值中的較小值。此值一旦決出,則會(huì )影響到數據在內存中的布局。一個(gè)有效對齊值為 n ,表示以下事實(shí):相關(guān)數據在內存中存放時(shí),其起始地址的值必須可以被 n 整除 。

  根據以上四條,可以很圓滿(mǎn)地解釋 S1 和 S2 的大小不同這一現狀。由于沒(méi)有使用 pragma pack 指示,那么編譯器(在我的測試環(huán)境下)會(huì )采用缺省的對齊值 4 。假設 S1 或者 S2 的實(shí)例將從地址 0x0000 處開(kāi)始。

  在 S1 中,第一個(gè)成員 i 的自身對齊值為 4 ,指定對齊值(盡管是缺省的)也是 4 ,同時(shí) 0x0000 這一地址符合被 4 整除的要求,因此, i 將占據 0x0000 到 0x0003 的四個(gè)字節,下一個(gè)可用地址值為 0x0004 ;接下來(lái)的成員c 的數據類(lèi)型為 char ,自身對齊值為 1 ,指定對齊值為 4 ,取較小者仍然是 1 , 0x0004 符合被 1 整除的要求,因此 c 將占據 0x0004 處的一個(gè)字節,下一個(gè)可用地址值為 0x0005 ;最后的一個(gè)成員 s 數據類(lèi)型為 short ,自身對齊值為 2 ,指定對齊值為 4 ,有效對齊值取 2 ,但是地址 0x0005 不能符合被 2 整除的要求,因此編譯器作相應調整,向后移動(dòng)到最近的滿(mǎn)足要求的地址處,即 0x0006 , s 將占用 0x0006 和 0x0007 處的兩個(gè)字節,由此導致S1 的大小為 8 。

  在地址 0x0005 處的一個(gè)字節,習慣上稱(chēng)之為填充數據( padding )。

  同理可以輕易推出 S2 結構的大小確實(shí)是 12 。是這樣嗎?不是的。實(shí)際動(dòng)手的結果應該是 10 。那么 12 應該作何解釋?

  我們來(lái)設想一個(gè)場(chǎng)景,程序員用 new 或者 malloc 分配一個(gè) S2 的數組。不用多,假定有兩個(gè)元素,而地址0x0000 處正好有空閑的內存可以滿(mǎn)足這一內存分配請求。我們都知道,在 C/C++ 語(yǔ)言中,數組的元素是緊鄰排放的。也就是說(shuō),后一個(gè)元素的起始地址應該正好等于前一個(gè)元素的起始地址,并加上元素的大小。我們來(lái)檢視一下S2 的情況,它的元素大小為 10 ,它的有效對齊值是 4 (請參閱概念及規則二),這表示任何一個(gè) S2 結構的起始地址都應該位于 4 的整數倍處?,F實(shí)的情況是,第一個(gè)元素的起始地址是 0x0000 ,第二個(gè)元素的起始地址變成了0x000A ,而后者的數值不能滿(mǎn)足被 4 整除的要求。正是為了解決這一情況,編譯器為 S2 結構在結尾處也增加了兩個(gè)字節的填充,從而滿(mǎn)足各個(gè)條件的限定。

  pragma pack 指示非常有效,使用也比較普遍,但是對于 平臺,它有一些力所不及的地方,我們再來(lái)看一個(gè)例子。仍然用 S2 ,這一次,我們強制把它的字節對齊設定為 1 ,并同時(shí)定義了 S2 的一個(gè)全局變量 s2 。也即:

  #pragma pack(1)

  struct S2

  {

  char c;

  int i;

  short s;

  } s2;

  #pragma pop()

  然后,在某處具有如下的數據訪(fǎng)問(wèn):

  int i = s2.i;

  這條看上去稀松平常的語(yǔ)句很可能不能如所希望的那樣執行。因為對于 i 的訪(fǎng)問(wèn)其前提應該是 i 的起始地址是 4的倍數(注意,這個(gè)不是對齊規則的約束結果,而是 CPU 的數據訪(fǎng)問(wèn)規則的約束結果),但強行指定的 1 字節對齊則導致 i 的起始地址是一個(gè)奇數。

  RVCT 編譯器為此做了特別的努力,引入了 __packed 關(guān)鍵字。此關(guān)鍵字應用到用戶(hù)定義數據結構上會(huì )導致該結構的內存布局取得與 pragma pack(1) 等同的效果,但是,更進(jìn)一步地,編譯器會(huì )把對該結構中成員的訪(fǎng)問(wèn)作適當的處理,發(fā)現不對齊的訪(fǎng)問(wèn)則會(huì )翻譯為調用適當的保證數據正確性的函數。此關(guān)鍵字也可以應用到指針上,以保證經(jīng)由指針對目標對象的訪(fǎng)問(wèn)也采用保守方式??梢灶A料到的是,此關(guān)鍵字的使用會(huì )降低代碼執行的效率,所以需要慎用,一個(gè)很典型的使用場(chǎng)景是移植其他平臺的代碼時(shí)。以下是一些使用了此關(guān)鍵字的定義示例:

  typedef __packed struct

  {

  char x; // 所有成員都會(huì )被 __packed 修飾

  int y;

  } X; // 5 字節的結構,自身對齊值 = 1

  int f(X* p)

  {

  return p->y; // 執行一個(gè)非對齊的讀取操作

  }

  typedef struct

  {

  short x;

  char y;

  __packed int z; // 僅 __pack 本成員,此用法僅適用于整型

  char a;

  } Y; // 8 字節結構,自身對齊值 = 2(請思考原因)

  int g(Y* p)

  {

  return p->z + p->x; // 僅對 z 執行非對齊讀取操作

  }

  需要注意的是, GCCE 編譯器沒(méi)有實(shí)現類(lèi)似的努力,它有一個(gè)和對齊有關(guān)的關(guān)鍵字: __attribute__ (packed)),該關(guān)鍵字的功效與 pragma pack(1) 類(lèi)似。

  8、 思考 / 練習題

  a) 位( bit )在字節中的排列,應該也有類(lèi)似字節序那樣的問(wèn)題,為什么沒(méi)有?

  b) 自己寫(xiě)幾個(gè)結構,根據規則推斷其大小,然后寫(xiě)代碼驗證

  c) 請查閱 RVCT 的相關(guān)文檔,學(xué)習 __align 關(guān)鍵字的含義和用法

  d) 了解微軟公司針對 Windows Mobile 平臺的編譯器是否也具有幫助程序員自動(dòng)解決對其訪(fǎng)問(wèn)的機制

  9、 參考資料

  a) 《編程卓越之道》,第一卷

  b) 《 RealView Compilation Tools - Compiler and Libraries Guide 》

  c) Information Center

  d) http://blog.csdn.net/xhfwr/archive/2006/07/23/963793.aspx


上一頁(yè) 1 2 下一頁(yè)

關(guān)鍵詞: 字節 ARM

評論


相關(guān)推薦

技術(shù)專(zhuān)區

關(guān)閉
国产精品自在自线亚洲|国产精品无圣光一区二区|国产日产欧洲无码视频|久久久一本精品99久久K精品66|欧美人与动牲交片免费播放
<dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><small id="yhprb"></small><dfn id="yhprb"></dfn><small id="yhprb"><delect id="yhprb"></delect></small><small id="yhprb"></small><small id="yhprb"></small> <delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"></dfn><dfn id="yhprb"></dfn><s id="yhprb"><noframes id="yhprb"><small id="yhprb"><dfn id="yhprb"></dfn></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><small id="yhprb"></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn> <small id="yhprb"></small><delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn>