亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > C > 正文

淺析內存對齊與ANSI C中struct型數據的內存布局

2020-01-26 15:54:38
字體:
來源:轉載
供稿:網友

這些問題或許對不少朋友來說還有點模糊,那么本文就試著探究它們背后的秘密。

首先,至少有一點可以肯定,那就是ANSI C保證結構體中各字段在內存中出現的位置是隨它們的聲明順序依次遞增的,并且第一個字段的首地址等于整個結構體實例的首地址。比如有這樣一個結構體:

復制代碼 代碼如下:

  struct vector{int x,y,z;} s;
  int *p,*q,*r;
  struct vector *ps;
  p = &s.x;
  q = &s.y;
  r = &s.z;
  ps = &s;

  assert(p < q);
  assert(p < r);
  assert(q < r);
  assert((int*)ps == p);
  // 上述斷言一定不會失敗


這時,有朋友可能會問:"標準是否規定相鄰字段在內存中也相鄰?"。 唔,對不起,ANSI C沒有做出保證,你的程序在任何時候都不應該依賴這個假設。那這是否意味著我們永遠無法勾勒出一幅更清晰更精確的結構體內存布局圖?哦,當然不是。不過先讓我們從這個問題中暫時抽身,關注一下另一個重要問題――――內存對齊。

許多實際的計算機系統對基本類型數據在內存中存放的位置有限制,它們會要求這些數據的首地址的值是某個數k(通常它為4或8)的倍數,這就是所謂的內存對齊,而這個k則被稱為該數據類型的對齊模數(alignment modulus)。當一種類型S的對齊模數與另一種類型T的對齊模數的比值是大于1的整數,我們就稱類型S的對齊要求比T強(嚴格),而稱T比S弱(寬松)。這種強制的要求一來簡化了處理器與內存之間傳輸系統的設計,二來可以提升讀取數據的速度。比如這么一種處理器,它每次讀寫內存的時候都從某個8倍數的地址開始,一次讀出或寫入8個字節的數據,假如軟件能保證double類型的數據都從8倍數地址開始,那么讀或寫一個double類型數據就只需要一次內存操作。否則,我們就可能需要兩次內存操作才能完成這個動作,因為數據或許恰好橫跨在兩個符合對齊要求的8字節內存塊上。某些處理器在數據不滿足對齊要求的情況下可能會出錯,但是Intel的IA32架構的處理器則不管數據是否對齊都能正確工作。不過Intel奉勸大家,如果想提升性能,那么所有的程序數據都應該盡可能地對齊。Win32平臺下的微軟C編譯器(cl.exe for 80x86)在默認情況下采用如下的對齊規則: 任何基本數據類型T的對齊模數就是T的大小,即sizeof(T)。比如對于double類型(8字節),就要求該類型數據的地址總是8的倍數,而char類型數據(1字節)則可以從任何一個地址開始。Linux下的GCC奉行的是另外一套規則(在資料中查得,并未驗證,如錯誤請指正):任何2字節大小(包括單字節嗎?)的數據類型(比如short)的對齊模數是2,而其它所有超過2字節的數據類型(比如long,double)都以4為對齊模數。

現在回到我們關心的struct上來。ANSI C規定一種結構類型的大小是它所有字段的大小以及字段之間或字段尾部的填充區大小之和。嗯?填充區?對,這就是為了使結構體字段滿足內存對齊要求而額外分配給結構體的空間。那么結構體本身有什么對齊要求嗎?有的,ANSI C標準規定結構體類型的對齊要求不能比它所有字段中要求最嚴格的那個寬松,可以更嚴格(但此非強制要求,VC7.1就僅僅是讓它們一樣嚴格)。我們來看一個例子(以下所有試驗的環境是Intel Celeron 2.4G + WIN2000 PRO + vc7.1,內存對齊編譯選項是"默認",即不指定/Zp與/pack選項):

復制代碼 代碼如下:

  typedef struct ms1
  {
     char a;
     int b;
  } MS1;

假設MS1按如下方式內存布局(本文所有示意圖中的內存地址從左至右遞增):
       _____________________________

       |   a   |        b          |

       +---------------------------+
Bytes:    1             4
因為MS1中有最強對齊要求的是b字段(int),所以根據編譯器的對齊規則以及ANSI C標準,MS1對象的首地址一定是4(int類型的對齊模數)的倍數。那么上述內存布局中的b字段能滿足int類型的對齊要求嗎?嗯,當然不能。如果你是編譯器,你會如何巧妙安排來滿足CPU的癖好呢?呵呵,經過1毫秒的艱苦思考,你一定得出了如下的方案:
       _______________________________________
       |       |///////////|                 |
       |   a   |//padding//|       b         |
       |       |///////////|                 |
       +-------------------------------------+
Bytes:    1         3             4
這個方案在a與b之間多分配了3個填充(padding)字節,這樣當整個struct對象首地址滿足4字節的對齊要求時,b字段也一定能滿足int型的4字節對齊規定。那么sizeof(MS1)顯然就應該是8,而b字段相對于結構體首地址的偏移就是4。非常好理解,對嗎?現在我們把MS1中的字段交換一下順序:

復制代碼 代碼如下:

  typedef struct ms2
  {
     int a;
     char b;
  } MS2;

或許你認為MS2比MS1的情況要簡單,它的布局應該就是

       _______________________

       |     a       |   b   |

       +---------------------+
Bytes:      4           1
因為MS2對象同樣要滿足4字節對齊規定,而此時a的地址與結構體的首地址相等,所以它一定也是4字節對齊。嗯,分析得有道理,可是卻不全面。讓我們來考慮一下定義一個MS2類型的數組會出現什么問題。C標準保證,任何類型(包括自定義結構類型)的數組所占空間的大小一定等于一個單獨的該類型數據的大小乘以數組元素的個數。換句話說,數組各元素之間不會有空隙。按照上面的方案,一個MS2數組array的布局就是:

|<-    array[1]     ->|<-    array[2]     ->|<- array[3] .....
__________________________________________________________

|     a       |   b   |      a       |   b  |.............

+----------------------------------------------------------
Bytes:  4         1          4           1

當數組首地址是4字節對齊時,array[1].a也是4字節對齊,可是array[2].a呢?array[3].a ....呢?可見這種方案在定義結構體數組時無法讓數組中所有元素的字段都滿足對齊規定,必須修改成如下形式:
       ___________________________________
       |             |       |///////////|
       |     a       |   b   |//padding//|
       |             |       |///////////|
       +---------------------------------+
Bytes:      4           1         3

現在無論是定義一個單獨的MS2變量還是MS2數組,均能保證所有元素的所有字段都滿足對齊規定。那么sizeof(MS2)仍然是8,而a的偏移為0,b的偏移是4。

好的,現在你已經掌握了結構體內存布局的基本準則,嘗試分析一個稍微復雜點的類型吧。

復制代碼 代碼如下:

  typedef struct ms3
  {
     char a;
     short b;
     double c;
  } MS3;

我想你一定能得出如下正確的布局圖:

        padding 

      _____v_________________________________
      |   |/|     |/////////|               |
      | a |/|  b  |/padding/|       c       |
      |   |/|     |/////////|               |
      +-------------------------------------+
Bytes:  1  1   2       4            8

sizeof(short)等于2,b字段應從偶數地址開始,所以a的后面填充一個字節,而sizeof(double)等于8,c字段要從8倍數地址開始,前面的a、b字段加上填充字節已經有4 bytes,所以b后面再填充4個字節就可以保證c字段的對齊要求了。sizeof(MS3)等于16,b的偏移是2,c的偏移是8。接著看看結構體中字段還是結構類型的情況:
復制代碼 代碼如下:

  typedef struct ms4
  {
     char a;
     MS3 b;
  } MS4;

MS3中內存要求最嚴格的字段是c,那么MS3類型數據的對齊模數就與double的一致(為8),a字段后面應填充7個字節,因此MS4的布局應該是:
       _______________________________________
       |       |///////////|                 |
       |   a   |//padding//|       b         |
       |       |///////////|                 |
       +-------------------------------------+
 Bytes:    1         7             16

顯然,sizeof(MS4)等于24,b的偏移等于8。

在實際開發中,我們可以通過指定/Zp編譯選項來更改編譯器的對齊規則。比如指定/Zpn(VC7.1中n可以是1、2、4、8、16)就是告訴編譯器最大對齊模數是n。在這種情況下,所有小于等于n字節的基本數據類型的對齊規則與默認的一樣,但是大于n個字節的數據類型的對齊模數被限制為n。事實上,VC7.1的默認對齊選項就相當于/Zp8。仔細看看MSDN對這個選項的描述,會發現它鄭重告誡了程序員不要在MIPS和Alpha平臺上用/Zp1和/Zp2選項,也不要在16位平臺上指定/Zp4和/Zp8(想想為什么?)。改變編譯器的對齊選項,對照程序運行結果重新分析上面4種結構體的內存布局將是一個很好的復習。

到了這里,我們可以回答本文提出的最后一個問題了。結構體的內存布局依賴于CPU、操作系統、編譯器及編譯時的對齊選項,而你的程序可能需要運行在多種平臺上,你的源代碼可能要被不同的人用不同的編譯器編譯(試想你為別人提供一個開放源碼的庫),那么除非絕對必需,否則你的程序永遠也不要依賴這些詭異的內存布局。順便說一下,如果一個程序中的兩個模塊是用不同的對齊選項分別編譯的,那么它很可能會產生一些非常微妙的錯誤。如果你的程序確實有很難理解的行為,不防仔細檢查一下各個模塊的編譯選項。

思考題:請分析下面幾種結構體在你的平臺上的內存布局,并試著尋找一種合理安排字段聲明順序的方法以盡量節省內存空間。

復制代碼 代碼如下:

    A. struct P1 { int a; char b; int c; char d; };
    B. struct P2 { int a; char b; char c; int d; };
    C. struct P3 { short a[3]; char b[3]; };
    D. struct P4 { short a[3]; char *b[3]; };
    E. struct P5 { struct P2 *a; char b; struct P1 a[2];  };

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表

圖片精選

亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
日韩小视频网址| 55夜色66夜色国产精品视频| 91精品91久久久久久| 国产欧美欧洲在线观看| 中文字幕av一区二区三区谷原希美| 一区二区三区在线播放欧美| 综合欧美国产视频二区| 国产精品久久久久91| 国产精品极品在线| 另类少妇人与禽zozz0性伦| 在线播放精品一区二区三区| 中文字幕久久亚洲| 精品国产一区二区三区久久久| 一区二区三区在线播放欧美| 日韩在线精品视频| 久久777国产线看观看精品| 日韩一级裸体免费视频| 亚洲男人天堂久| 日韩在线视频中文字幕| 国产精品影片在线观看| 亚洲福利视频网站| 亚洲自拍偷拍福利| 精品美女久久久久久免费| 国产精品久久久久久久7电影| 欧美激情在线一区| 精品国产老师黑色丝袜高跟鞋| 亚洲人精选亚洲人成在线| 亚洲第一精品自拍| 成人亚洲激情网| 91成人在线视频| 国内精品久久久久久中文字幕| 久久久久99精品久久久久| 97香蕉久久超级碰碰高清版| 日本中文字幕不卡免费| 久久久久久美女| 欧美日韩在线观看视频小说| 国产精品自拍视频| 亚洲曰本av电影| 国产精品吹潮在线观看| 久久久精品国产| 日韩人在线观看| 91久久精品国产| 久久久久久香蕉网| 大胆欧美人体视频| 久久久久久久97| 欧美成人免费观看| 亚洲乱码一区av黑人高潮| 97在线观看视频| 国产一区二区三区直播精品电影| 久久久成人精品| 午夜精品久久久久久99热| 日韩中文字幕免费视频| 亚洲男子天堂网| 欧美另类老肥妇| 久久久精品国产一区二区| 88国产精品欧美一区二区三区| 久久午夜a级毛片| 午夜精品蜜臀一区二区三区免费| 欧美体内谢she精2性欧美| 激情成人在线视频| 亚洲免费小视频| 国产欧美日韩精品丝袜高跟鞋| 2019国产精品自在线拍国产不卡| 欧美日韩综合视频| 欧美日韩ab片| 97热精品视频官网| 亚洲国产精品嫩草影院久久| 中文字幕无线精品亚洲乱码一区| 亚洲精品一区二区在线| 久久久久久久网站| 欧日韩不卡在线视频| 中文字幕av一区中文字幕天堂| 成人精品视频在线| 亚洲精品国产欧美| 国产一区二区色| 色婷婷av一区二区三区久久| 国产成人精品一区二区| 日韩黄色av网站| 欧美黄色片免费观看| 欧美精品videosex性欧美| 国产色婷婷国产综合在线理论片a| 久久久免费观看视频| 91av在线免费观看视频| 国产福利精品视频| 国产精品成人av性教育| 91精品国产91久久久久福利| 91精品免费久久久久久久久| 久青草国产97香蕉在线视频| 欧美日韩激情美女| 韩日欧美一区二区| 欧洲美女免费图片一区| 色一情一乱一区二区| 久久久国产精品视频| 国产91成人video| 亚洲男人av在线| 久久久久久久999精品视频| 日韩av成人在线| 日韩av一区二区在线观看| 7m精品福利视频导航| 国产福利视频一区二区| 成人激情免费在线| 国产日韩精品一区二区| 日韩中文字幕不卡视频| 成人a在线观看| 不用播放器成人网| 国产精品久久久久影院日本| 成人免费观看a| 欧美日韩国产一区在线| 亚洲一区亚洲二区亚洲三区| 久久国产色av| 欧美性开放视频| 一区二区三区四区视频| 91丨九色丨国产在线| 国内精品在线一区| 2019精品视频| 国产欧美一区二区三区久久人妖| 日韩欧美中文在线| 国产精品盗摄久久久| 欧美日韩在线影院| 久国内精品在线| 欧美日韩美女在线| 国产在线拍揄自揄视频不卡99| 国产视频亚洲视频| 久久综合久久美利坚合众国| 色妞在线综合亚洲欧美| 成人欧美一区二区三区黑人| 国产精品久久不能| 亚洲深夜福利在线| 国产日韩在线观看av| 亚洲成人网久久久| 国产在线不卡精品| 97超级碰在线看视频免费在线看| 亚洲国产高清高潮精品美女| 青青青国产精品一区二区| 青青精品视频播放| 日韩激情视频在线播放| 92版电视剧仙鹤神针在线观看| 国产精品免费网站| 国产成人一区二区三区电影| 亚洲影影院av| 中文字幕av一区二区三区谷原希美| 一区二区三区在线播放欧美| 欧美一级电影久久| 欧美成人精品在线观看| 亚洲精品成人免费| www.日韩欧美| 高清在线视频日韩欧美| 国产精品99久久久久久人| 欧美在线精品免播放器视频| …久久精品99久久香蕉国产| 国产精品精品一区二区三区午夜版| 亚洲国产精品资源| 欧美一区二区三区四区在线| 91久久精品国产91性色| 亚洲奶大毛多的老太婆| 国产精品xxx视频| 91精品成人久久| 亚洲国产日韩欧美综合久久| 欧洲亚洲免费视频| 欧美激情视频播放| 日产精品99久久久久久| 亚洲欧美国产精品久久久久久久| 中文字幕精品一区二区精品| 亚洲精选一区二区|