1. 程式人生 > >結構體4位元組對齊規則的詳解

結構體4位元組對齊規則的詳解

一 四位元組對齊的規則

C++中結構體變數的儲存為什麼有個4位元組對齊的規則,這裡是假設32位機器上,CPU在讀取記憶體資料的時候4位元組對齊會取得更快的速度;這是因為:1位元組8位,4位元組正好32位,而32位機器的暫存器,地址什麼的都是32位的,正好一次處理就完成。

二 相關內容解釋

例如,下面的結構各成員空間分配情況:

  1. struct test  
  2. {  
  3.      char x1;  
  4.      short x2;  
  5.      float x3;  
  6.      char x4;  
  7. };  
“結構的第一個成員x1,其偏移地址為0,佔據了第1個位元組。第二個成員x2為short型別,其起始地址必須2位元組對界,因此,編譯器在x2和x1之間填充了一個空位元組。結構的第三個成員x3和第四個成員x4恰好落在其自然對界地址上,在它們前面不需要額外的填充位元組。在test結構中,成員x3要求4位元組對界,是該結構所有成員中要求的最大對界單元,因而test結構的自然對界條件為4位元組,編譯器在成員x4後面填充了3個空位元組。整個結構所佔據空間為12位元組。更改C編譯器的預設位元組對齊方式。”


1、這個意思就是說前兩個成員各佔2位元組,後兩個成員各佔4個位元組,換句話說,每個成員佔的位元組數都和鄰近它的前一個成員型別相關。
2、__attribute__ ((packed)),取消結構在編譯過程中的優化對齊,按照實際佔用位元組數進行對齊。用這種方式做的缺點是什麼呢?是不是因為以前計算資源有限,所以要通過這種位元組對齊來節省資源,但現在硬體資源充足了,因此不用過多考慮這個問題?
3、#pragma pack(1) // 按照1位元組方式進行對齊
  1. struct TCPHEADER  
  2. {  
  3.      short SrcPort; // 16位源埠號
  4.      short DstPort; // 16位目的埠號
  5.      int SerialNo; // 32位序列號
  6.      int AckNo; // 32位確認號
  7.      unsigned char HaderLen : 4; // 4位首部長度
  8.      unsigned char Reserved1 : 4; // 保留6位中的4位
  9.      unsigned char Reserved2 : 2; // 保留6位中的2位
  10.      unsigned char URG : 1;  
  11.      unsigned char ACK : 1;  
  12.      unsigned char PSH : 1;  
  13.      unsigned char RST : 1;  
  14.      unsigned char SYN : 1;  
  15.      unsigned char FIN : 1;  
  16.      short WindowSize; // 16位視窗大小
  17.      short TcpChkSum; // 16位TCP檢驗和
  18.      short UrgentPointer; // 16位緊急指標
  19. };  
  1. #pragma pack() // 取消1位元組對齊方式
這段code表明所有的成員都按照1位元組對齊?那像short型別,只取第一個位元組的值,後三個忽略?

4、每種資料型別是低位在前,還是高位在前呢?如何判斷?根據OS或者其它的條件?

位元組對齊在不同編譯器下語法是不一樣的,在GCC中是#pragma push(1)  #pragma pack(); 在MS C++中用VC的程式碼項裡可以調整,預設是8位元組;

  1. typedefstruct
  2. {  
  3.    char c;  
  4.    int  i;  
  5. }test;  
位元組對齊,是對齊,比如說char 與 int 如果是4位元組對齊,那麼char也會佔用4個位元組,總共佔8位元組,而且結構體物件儲存是按照順序存的,char 肯定在int前面。第二種情況如果1位元組對齊,意味著char只佔1位元組,而結下來int會佔用4位元組,這個N位元組對齊的意思是,每個成員佔用空間必須是N位元組的倍數,不足N位元組的佔用N位元組。那麼以1位元組對齊那它佔用5個位元組。
還有每種資料是低位還是高位在前,這個根處理器有關,Intel處理是小端對齊,比如說一個整數522387969用16進製表示是:0x1f 23 02 01,在Intel處理器中表示是0x01 02 23 1f,所以在記憶體用0x01 02 03 1f來示522387969,這就是所謂有小端對齊;但在arm處理器中522387969表示是0x1f 23 02 01,這就是所謂的大端對齊,這種方式又叫作網路位元組序。

三 實踐例子分析

例子1:

  1. struct A  
  2. {  
  3.     bool a1;  
  4.     bool a2;  
  5.     int a;  
  6.     bool a3;  
  7. };  
  8. struct B  
  9. {  
  10.     bool a1;  
  11.     bool a2;  
  12.     bool a3;  
  13.     int a;  
  14. };  
  15. struct C  
  16. {  
  17.     bool a1;          
  18.     int a;  
  19.     bool a2;  
  20.     bool a3;  
  21. };  
  22. bool t;  
  23. printf("Sizeof(bool) = %d,Sizeof(A) = %d,Sizeof(B) = %d,Sizeof(C) = %d\n",sizeof(t),sizeof(A),sizeof(B),sizeof(C));  
答案為:1,12,8,12

例子2:

  1. struct s1  
  2. {  
  3.     int m1;  
  4.     char m2;  
  5.     char m3;  
  6. };  
  7. struct s2  
  8. {  
  9.     char m2;  
  10.     int m1;  
  11.     char m3;  
  12. };  
  13. struct s3  
  14. {  
  15.     char m2;  
  16.     char m3;  
  17.     int m1;  
  18. };  
  19. printf("Sizeof(s1) = %d,Sizeof(s2) = %d,Sizeof(s3) = %d\n",sizeof(s1),sizeof(s2),sizeof(s3));  
答案為:8,12,8

四 相關分析

現代計算機中記憶體空間都是按照byte劃分的,從理論上講似乎對任何型別的變數的訪問可以從任何地址開始,但實際情況是在訪問特定型別變數的時候經常在特 定的記憶體地址訪問,這就需要各種型別資料按照一定的規則在空間上排列,而不是順序的一個接一個的排放,這就是對齊。

各個硬體平臺對儲存空間的處理上有很大的不同。一些平臺對某些特定型別的資料只能從某些特定地址開始存取。比如有些架構的CPU在訪問 一個沒有進行對齊的變數的時候會發生錯誤,那麼在這種架構下程式設計必須保證位元組對齊.其他平臺可能沒有這種情況,但是最常見的是如果不按照適合其平臺要求對 資料存放進行對齊,會在存取效率上帶來損失。比如有些平臺每次讀都是從偶地址開始,如果一個int型(假設為32位系統)如果存放在偶地址開始的地方,那 麼一個讀週期就可以讀出這32bit,而如果存放在奇地址開始的地方,就需要2個讀週期,並對兩次讀出的結果的高低位元組進行拼湊才能得到該32bit數 據。

其實位元組對齊的細節和具體編譯器實現相關,但一般而言,滿足三個準則:1) 結構體變數的首地址能夠被其最寬基本型別成員的大小所整除;2) 結構體每個成員相對於結構體首地址的偏移量都是成員大小的整數倍,如有需要編譯器會在成員之間加上填充位元組;例如上面第二個結構體變數的地址空間。3) 結構體的總大小為結構體最寬基本型別成員大小的整數倍,如有需要編譯器會在最末一個成員之後加上填充位元組。

五 小結與領悟

進行位元組對齊的原因是因為處理器在存取記憶體的時候並不是一個位元組一個位元組操作的, 通常他會一次存取多個位元組, 比如四個。所以將資料以四位元組對齊方式儲存能夠提高資料存取效率(其實具體的儲存和對齊方式沒那麼簡單,不說了)。但是, 有時候這種預設的優化並不是我們想要的。比如在設計網路程式的時候,一般情況下我們會定義一個結構體來表示應用程協議的協議頭,如果通訊雙方的程式是在不同體系結構的計算機編譯出來的(這很有可能),那麼預設的對齊方式是有可能是不同的,這樣解析出來的協議頭必然就是錯的。 另外即使很幸運的對齊方式一樣,在協議頭裡面插入了幾個無關的位元組那也是很不優雅的何況還佔用頻寬。

還好,這種對齊方式我們是可以控制的,一般地,可以通過下面的方法來改變預設的對齊方式:

  · 使用偽指令#pragma pack (n),編譯器將按照n個位元組對齊;

  · 使用偽指令#pragma pack (),取消自定義位元組對齊方式。 

注意:如果#pragma pack (n)中指定的n大於結構體中最大成員的size,則其不起作用,結構體仍然按照size最大的成員進行對界。

相關推薦

結構4位元組規則

一 四位元組對齊的規則 C++中結構體變數的儲存為什麼有個4位元組對齊的規則,這裡是假設32位機器上,CPU在讀取記憶體資料的時候4位元組對齊會取得更快的速度;這是因為:1位元組8位,4位元組正好32位,而32位機器的暫存器,地址什麼的都是32位的,正好一次處理就完成。

結構位元組

在用sizeof運算子求算某結構體所佔空間時,並不是簡單地將結構體中所有元素各自佔的空間相加,這裡涉及到記憶體位元組對齊的問題。 1) 結構體變數的首地址能夠被其最寬基本型別成員的大小所整除; 2) 結構體每個成員相對結構體首地址的偏移量都是成員大小的整數倍; 3) 結構體的總大小為結構

C語言結構位元組例項【C語言筆試題】

 一、筆試題目:在一個64位的作業系統中定義如下結構體: <span style="font-family:Microsoft YaHei;font-size:12px;">struct st_task { uint16_t id; uint32

C語言結構位元組原則

為什麼要對齊?     現代計算機中記憶體空間都是按照byte劃分的,從理論上講似乎對任何型別的變數的訪問可以從任何地址開始,但實際情況是在訪問特定型別變數的時候經常在特 定的記憶體地址訪問,這就需要各種型別資料按照一定的規則在空間上排列,而不是順序的一個接一個的排放,

C++中結構位元組問題

  前不久,在C++程式中碰到一個有關結構體位元組對齊的問題。 一。問題描述 在程式中,定義了一個結構體,如下:typedef struct{   char name[33];   int ID;   int  age;} PERSON; 聲明瞭一個該結構體的陣列:PERSO

C語言位元組問題

部落格園 首頁 新隨筆 聯絡 管理 訂閱 隨筆- 80  文章- 0  評論- 125  引言      考慮下面的結構體定義: 1 typedef struct{ 2 char c1; 3 short s; 4

c++記憶體中位元組問題

struct MyStruct  {  double dda1;  char dda;  int type  };  對結構MyStruct採用sizeof會出現什麼結果呢?sizeof(MyStruct)為多少呢?也許你會這樣求:  sizeof(MyStruct)=sizeof(double)+sizeo

結構為什麼要4位元組

sizeof與struct——求結構體大小的計算方法 sizeof淺析(一)——求結構體大小 這篇文章講了sizeof求結構體大小的計算方法,並給出可以套用的準則: 一、儲存變數時地址要求對齊,編譯器在編譯程式時會遵循兩條原則: (1)結構體變數

C/C++結構方式,從記憶體地址進行解析

注意:童鞋們如果仔仔細細看完這篇部落格,肯定能明白結構體的對齊方式。 最近在做一個專案的時候,客戶給的鐳射點雲檔案是二進位制形式,因此需要根據客戶定義的結構體,將點雲檔案儲存為文字檔案方便在第三方軟體如cloudCompare中檢視。但是發現客戶的結構體所佔記憶體空間跟我的

c++資料型別(整型/浮點型/字串/陣列/引用/結構(記憶體)/類(虛擬函式))定義、所佔位元組數、最大最小值

#include<iostream> #include<string> #include<limits> using namespace std; int main() { cout << "type: \t\t" << "********

C/C++ 結構字節

sizeof 情況下 logs 微軟 變量的存儲 add cout 數據 syntax 在用sizeof運算符求算某結構體所占空間時,並不是簡單地將結構體中所有元素各自占的空間相加,這裏涉及到內存字節對齊的問題。從理論上講,對於任何 變量的訪問都可以從任何地址開

C#.NET結構字節[轉載]

第一個 屬性 設置 匯編 sum pos char bsp internal [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)] internal struct DllInvoices

C語言結構字節簡單計算方法

最大的 一個數 數據類型 double short 位操作 結構體字節對齊 根據 struct 1.在C語言裏面每一種數據類型都有字節對齊比如在32位操作系統下:整型的自身對齊數就是 4 字節,字符型就是 1 字節,double就是 8 字節。 但是結構體的計算方式就和普通

C/C++結構用法及其原則

#include <iostream> using namespace std; /* ①結構體變數的首地址(比如結構體陣列就要考慮到結構體本身的地址)能夠被其最寬基本型別成員的大小所整除; ②結構體每個成員相對結構體首地址的偏移量(offset)都是該成員大小的整數倍,如

關於結構的記憶體

記憶體是以位元組為單位編號,但一些硬體平臺對某些特定型別的資料只能從某些特定地址開始,比如從偶地址開始。若不按照適合其平臺的要求對資料存放進行對齊,會影響到效率。因此,在記憶體中,各型別的資料是按照一定的規則在記憶體中存放的,這就是本文要研究的對齊問題。 本文以gcc

結構(記憶體)和共用—C語言

結構體 C語言學到現在,相信大家已經熟知了基本型別(整型、實型、字元型)的變數和一種構造型別資料(陣列),但是隻有這些資料型別是不夠的,因此我們接下來介紹C語言中可以將不同型別的定義自己的資料型別——結構體。 結構體與陣列的比較 由於結構體和陣列有很大的類似之處,所

結構中記憶體&&大端小端模式

(題目來自牛客網) 在一個64位的作業系統中定義如下結構體: struct st_task { uint16_t id; uint32_t value; uint64_t timestamp; }; 同時定義fool函式如下:

sizeof(結構)和記憶體以及位域

Win32平臺下的微軟C編譯器的對齊策略:1) 結構體變數的首地址能夠被其最寬基本型別成員的大小所整除;備註:編譯器在給結構體開闢空間時,首先找到結構體中最寬的基本資料型別,然後尋找記憶體地址能被該基本資料型別所整除的位置,作為結構體的首地址。將這個最寬的基本資料型別的大小作

點陣圖4位元組問題

在自己對影象資料進行處理的時候,會有位元組對其的問題,由於之前使用的影象大都是8bit或者是24bit,32bit的影象,使用的對其公式是(pixelwidth*channel+3)/4*4。後面也有看到有些寫法如:(width * bitCounts + 31) / 32

結構中的與補

    申請blog已經很長時間了,一直沒有時間往上寫點內容,正巧最近各種巧合需要重新學習一下Linux下的開發,將好久不用的C\C++重新研究了一通,在這裡就把一些我自己搞弄清楚的點寫一下,一來為了鞏固一下,二來給需要的朋友看一看,寫的不好請多指教。    關於結構體,從第