1. 程式人生 > >C++關於結構體對齊

C++關於結構體對齊

說明:
結構體的sizeof值,並不是簡單的將其中各元素所佔位元組相加,而是要考慮到儲存空間的位元組對齊問題。這些問題在平時程式設計的時候也確實不怎麼用到,但在一些筆試面試題目中出是常常出現,一、解釋
現代計算機中記憶體空間都是按照byte劃分的,從理論上講似乎對任何型別的變數的訪問可以從任何地址開始,但實際情況是在訪問特定型別變數的時候經常在特 定的記憶體地址訪問,這就需要各種型別資料按照一定的規則在空間上排列,而不是順序的一個接一個的排放,這就是對齊。
各個硬體平臺對儲存空間的處理上有很大的不同。一些平臺對某些特定型別的資料只能從某些特定地址開始存取。比如有些架構的CPU在訪問 一個沒有進行對齊的變數的時候會發生錯誤,那麼在這種架構下程式設計必須保證位元組對齊.其他平臺可能沒有這種情況,但是最常見的是如果不按照適合其平臺要求對 資料存放進行對齊,會在存取效率上帶來損失。比如有些平臺每次讀都是從偶地址開始,如果一個int型(假設為32位系統)如果存放在偶地址開始的地方,那 麼一個讀週期就可以讀出這32bit,而如果存放在奇地址開始的地方,就需要2個讀週期,並對兩次讀出的結果的高低位元組進行拼湊才能得到該32bit資料。

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

三、基本概念

位元組對齊:計算機儲存系統中以Byte為單位儲存資料,不同資料型別所佔的空間不同,如:整型(int)資料佔4個位元組,字元型(char)資料佔一個位元組,短整型(short)資料佔兩個位元組,等等。計算機為了快速的讀寫資料,預設情況下將資料存放在某個地址的起始位置,如:整型資料(int)預設儲存在地址能被4整除的起始位置,字元型資料(char)可以存放在任何地址位置(被1整除),短整型(short)資料儲存在地址能被2整除的起始位置。這就是預設位元組對齊方式。

四、結構體長度求法
1.成員都相同時(或含陣列且陣列資料型別同結構體其他成員資料型別):
結構體長度=成員資料型別長度×成員個數(各成員長度之和);
結構體中陣列長度=陣列資料型別長度×陣列元素個數;
2.成員不同且不含其它結構體時;
(1).分析各個成員長度;
(2).找出最大長度的成員長度M(結構體的長度一定是該成員的整數倍);
(3).並按最大成員長度出現的位置將結構體分為若干部分;
(4).各個部分長度一次相加,求出大於該和的最小M的整數倍即為該部分長度
(5).將各個部分長度相加之和即為結構體長度
3.含有其他結構體時:
(1).分析各個成員長度;
(2).對是結構體的成員,其長度按b來分析,且不會隨著位置的變化而變化;
(3).分析各個成員的長度(成員為結構體的分析其成員長度),求出最大值;
(4).若長度最大成員在為結構體的成員中,則按結構體成員為分界點分界;
其他成員中有最大長度的成員,則該成員為分界點;
求出各段長度,求出大於該和的最小M的整數倍即為該部分長度
(5).將各個部分長度相加之和即為結構體長度

五、空結構體

struct S5 { };
sizeof( S5 ); // 結果為1
“空結構體”(不含資料成員)的大小不為0,而是1。試想一個“不佔空間”的變數如何被取地址、兩個不同的“空結構體”變數又如何得以區分呢於是,“空結構體”變數也得被儲存,這樣編譯器也就只能為其分配一個位元組的空間用於佔位了。
六、有static的結構體
struct S4{
char a;
long b;
static long c; //靜態
};
靜態變數存放在全域性資料區內,而sizeof計算棧中分配的空間的大小,故不計算在內,S4的大小為4+4=8。

七、舉例說明
1.舉例1

很顯然預設對齊方式會浪費很多空間,例如如下結構:
struct student
{
char name[5];
int num;
short score;
}

本來只用了11bytes(5+4+2)的空間,但是由於int型預設4位元組對齊,存放在地址能被4整除的起始位置,即:如果name[5]從0開始存放,它佔5bytes,而num則從第8(偏移量)個位元組開始存放。所以sizeof(student)=16。於是中間空出幾個位元組閒置著。但這樣便於計算機快速讀寫資料,是一種以空間換取時間的方式。其資料對齊如下圖:
|char|char|char|char|
|char|—-|—-|—-|
|——–int——–|
|–short–|—-|—-|
如果我們將結構體中變數的順序改變為:
struct student
{
int num;
char name[5];
short score;
}

則,num從0開始存放,而name從第4(偏移量)個位元組開始存放,連續5個位元組,score從第10(偏移量)開始存放,故sizeof(student)=12。其資料對齊如下圖:
|——–int——–|
|char|char|char|char|
|char|—-|–short–|
如果我們將結構體中變數的順序再次改為為:
struct student
{
int num;
short score;
char name[5];
}

則,sizeof(student)=12。其資料對齊如下圖:
|——–int——–|
|–short–|char|char|
|char|char|char|—-|
2.舉例2
(1)
struct test1
  { int a;
   int b[4];
  };

sizeof(test1)=sizeof(int)+4*sizeof(int)=4+4*4=20;
(2)
struct test2
  { char a;
   int b;
   double c;
   bool d;
  };

分析:該結構體最大長度double型,長度是8,因此結構體長度分兩部分:
第一部分是a、 b、 c的長度和,長度分別為1,4,8,則該部分長度和為13,取8的大於13的最小倍數為16;
第二部分為d,長度為1,取大於1的8的最小倍數為8,
兩部分和為24,故sizeof(test2)=24;
(3)
struct test3
{
char a;
test2 bb;//見上題
int cc;
}

分析:該結構體有三個成員,其中第二個bb是型別為test2的結構體,長度為24,且該結構體最大長度成員型別為double型,以後成員中沒有double型,所以按bb分界為兩部分:
第一部分有a 、bb兩部分,a長度為1,bb長度為24,取8的大於25的最小倍數32;
第二部分有cc,長度為4,去8的大於4的最小倍數為8;
兩部分之和為40,故sizeof(test3)=40;

(4)
struct test4
{
char a;
int b;
};
struct test5
{ char c;
test4 d;
double e;
bool f;
};

求sizeof(test5)
分析:test5明顯含有結構體test4,按例2容易知道sizeof(test4)=8,且其成員最大長度為4;則結構體test5的最大成員長度為8(double 型),考試.大提示e是分界點,分test5為兩部分:
第一部分由c 、d、e組成,長度為1、8、8,故和為17,取8的大於17的最小倍數為24;
第二部分由f組成,長度為1,取8的大於1的最小倍數為8,
兩部分和為32,故sizeof(test5)=24+8=32;

八、union
union的長度取決於其中的長度最大的那個成員變數的長度。即union中成員變數是重疊擺放的,其開始地址相同。
其實union(共用體)的各個成員是以同一個地址開始存放的,每一個時刻只可以儲存一個成員,這樣就要求它在分配記憶體單元時候要滿足兩點:
1.一般而言,共用體型別實際佔用儲存空間為其最長的成員所佔的儲存空間;
2.若是該最長的儲存空間對其他成員的元型別(如果是陣列,取其型別的資料長度,例int a[5]為4)不滿足整除關係,該最大空間自動延伸;
我們來看看這段程式碼:
union mm{
char a;//元長度1
int b[5];//元長度4
double c;//元長度8
int d[3];
};
本來mm的空間應該是sizeof(int)*5=20;但是如果只是20個單元的話,那可以存幾個double型(8位)呢?兩個半?當然不可以,所以mm的空間延伸為既要大於20,又要滿足其他成員所需空間的整數倍,即24
所以union的儲存空間先看它的成員中哪個佔的空間最大,拿他與其他成員的元長度比較,如果可以整除就行。

九、指定對界

pragma pack()命令

如何修改編譯器的預設對齊值?
1.在VC IDE中,可以這樣修改:[Project]|[Settings],c/c++選項卡Category的Code Generation選項的Struct Member Alignment中修改,預設是8位元組。
2.在編碼時,可以這樣動態修改:#pragma pack .注意:是pragma而不是progma.
一般地,可以通過下面的方法來改變預設的對界條件:
使用偽指令#pragma pack (n),編譯器將按照n個位元組對齊;
使用偽指令#pragma pack (),取消自定義位元組對齊方式。
注意:如果#pragma pack (n)中指定的n大於結構體中最大成員size,則其不起作用,結構體仍然按照size最大的成員進行對界。
為了節省空間,我們可以在編碼時通過#pragma pack()命令指定程式的對齊方式,括號中是對齊的位元組數,若該命令括號中的內容為空,則為預設對齊方式。例如,對於上面第一個結構體,如果通過該命令手動設定對齊位元組數如下:

pragma pack(2) //設定2位元組對齊

struct strdent
{
char name[5]; //本身1位元組對齊,比2位元組對齊小,按1位元組對齊
int num; //本身4位元組對齊,比2位元組對齊大,按2位元組對齊
short score; //本身也2位元組對齊,仍然按2位元組對齊
}

pragma pack() // 恢復先前的pack設定,取消設定的位元組對齊方式

則,num從第6(偏移量)個位元組開始存放,score從第10(偏移量)個位元組開始存放,故sizeof(student)=12,其資料對齊如下圖:
|char|char|
|char|char|
|char|—-|
|—-int–|
|—-int–|
|–short–|
這樣改變預設的位元組對齊方式可以更充分地利用儲存空間,但是這會降低計算機讀寫資料的速度,是一種以時間換取空間的方式。

十、程式碼驗證
程式碼
//————————————
// 環境:VS2005
// 時間:2010.9.24
// 用途:結構體大小測試
// 作者:pppboy.blog.163.com
//———————————–

include “stdafx.h”

include

using namespace std;
//空
struct S0{ };
struct S1{
char a;
long b;
};
struct S2{
long b;
char a;
};
struct S3 {
char c;
struct S1 d;//結構體
long e;
};
struct S4{
char a;
long b;
static long c; //靜態
};
struct S5{
char a;
long b;
char name[5]; //陣列
};
//含有一個數組
struct S6{
char a;
long b;
int name[5]; //陣列
};
struct student0
{
char name[5];
int num;
short score;
};
struct student1
{
int num;
char name[5];
short score;
};
struct student2
{
int num;
short score;
char name[5];
};
union union1
{
long a;
double b;
char name[9];
};
union union2{
char a;
int b[5];
double c;
int d[3];
};
int main(int argc, char* argv[])
{
cout << “char: ” << sizeof(char) << endl; //1
cout << “long: ” << sizeof(long) << endl; //4
cout << “int: ” << sizeof(int) << endl; //4
cout << “S0: ” << sizeof(S0) << endl; //1
cout << “S1: ” << sizeof(S1) << endl; //8
cout << “S2: ” << sizeof(S2) << endl; //8
cout << “S3: ” << sizeof(S3) << endl; //24
cout << “S4: ” << sizeof(S4) << endl; //8
cout << “S5: ” << sizeof(S5) << endl; //16
cout << “S6: ” << sizeof(S6) << endl; //28
cout << “union1 :” << sizeof(union1) << endl;
cout << “union2 :” << sizeof(union2) << endl;
cout << “student0: ” << sizeof(student0) << endl;
cout << “student1: ” << sizeof(student1) << endl;
cout << “student2: ” << sizeof(student2) << endl;
system(“pause”);
return 0;
}
輸出
//這是預設的結果(8位元組對齊)
char: 1
long: 4
int: 4
S0: 1
S1: 8
S2: 8
S3: 16
S4: 8
S5: 16
S6: 28
union1 :16
union2 :24
student0: 16
student1: 12
student2: 12
請按任意鍵繼續…
//這是16位元組對齊的結果,可以看到當設定16位元組對齊時,確實沒什麼效果,裡面最大的是double,也就是8位元組,#pragma pack (n)中指定的n大於結構體中最大成員size,則其不起作用。
char: 1
long: 4
int: 4
double:8
S0: 1
S1: 8
S2: 8
S3: 16
S4: 8
S5: 16
S6: 28
union1 :16
union2 :24
student0: 16
student1: 12
student2: 12
請按任意鍵繼續…
//這是2位元組對齊的結果,可以慢慢參考研究
char: 1
long: 4
int: 4
double:8
S0: 1
S1: 6
S2: 6
S3: 12
S4: 6
S5: 12
S6: 26
union1 :10
union2 :20
student0: 12
student1: 12
student2: 12
請按任意鍵繼續…

說明:
(1)預設8位元組對齊
(2)分析
S0:空
S1:
|char|—-|—-|—-|
|——-long——–|
S2:
|——-long——–|
|char|—-|—-|—-|
S3:
其中包含的S1中最長的為long,S3中也為long,以最長的為分界,那麼為:1+8+4 = 13,那麼這個結構體的長度就是8的倍數16。
記憶體是怎麼樣的現在還沒有弄清楚。。。
S4:
靜態變數存放在全域性資料區內,而sizeof計算棧中分配的空間的大小,故不計算在內,S4的大小為4+4=8。
S5,S6,Student見上面例子。
union1:
最長double=8,但char c[9]用9個不夠,再加一倍到16.
union2:
型別最長的是long=8,變數最長的是int b[5] = 4*5=20,20以上8的倍數為24。

十一、還沒有解決的問題
雖然知道結構體中含有結構體的長度怎麼計算,但不知道它的記憶體是什麼樣子的,在VS中用
cout << “&objS3.a: “<< hex << &objS3.a << endl;
為什麼顯示出來是亂碼??
十二、位元組對齊可能帶來的隱患
(說明:從一個pdf複製,參考一下)
程式碼中關於對齊的隱患,很多是隱式的。比如在強制型別轉換的時候。例如:
unsigned int i = 0x12345678;
unsigned char *p=NULL;
unsigned short *p1=NULL;
p=&i;
*p=0x00;
p1=(unsigned short *)(p+1);
*p1=0x0000;

相關推薦

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

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

C-結構

技術群的筒子們有時候會提到結構體對齊,說實話這個問題還不是幾句話能講清楚的。這個問題網上一搜一大把,已經有無數的前輩總結過。看了很多網上的資料,根據我個人的一些理解,再總結一下,配了些圖片,希望大家能看懂。 首先是結構體對齊規則: 1、對於n個位元組的元素,它的首地址要能

c結構深刻理解

1. 先看下面的例子: struct A{    char c1;    int i;    short s;    int j; }a; struct B{    int i;    int j;      short s;    char c1; }b; 結構A沒有遵守位元組對齊原則(為了區分,我將它叫做

c++結構

參考各資料得出自己使用的公式:1、(當前偏移量+當前填充數)%當前變數大小=02、(總偏移大小+末尾填充數)%最寬變數大小=0必須先滿足1、再滿足2。例如程式碼如下:struct A{int a;//(當前偏移量0+當前填充數0)%當前變數大小4=0char b;// (當前

C/C++結構_思索

       最近在看對齊方面的問題,發現大家在面試筆試的時候,對方基本上都是會拿包含陣列的結構體、或者包含結構體的結構體來考大家,而不會單純的拿幾個int,long,或者double組合在一起考,因為那樣太簡單,找了些資料,幫助自己也幫助大家理解下,共同交流!      

C語言結構(記憶體問題)

C語言結構體對齊也是老生常談的話題了。基本上是面試題的必考題。內容雖然很基礎,但一不小心就會弄錯。寫出一個struct,然後sizeof,你會不會經常對結果感到奇怪?sizeof的結果往往都比你宣告的變數總長度要大,這是怎麼回事呢?     開始學的時候,

C++關於結構

說明: 結構體的sizeof值,並不是簡單的將其中各元素所佔位元組相加,而是要考慮到儲存空間的位元組對齊問題。這些問題在平時程式設計的時候也確實不怎麼用到,但在一些筆試面試題目中出是常常出現,一、解釋 現代計算機中記憶體空間都是按照byte劃分的,從理論上講

C語言結構與不對設定總結

相信不同的編譯平臺間的預設設定差異給大家帶來了很多困擾。在此,僅就結構體對齊解析下之間的差異設定方法。 1.gcc中結構體預設是4個位元組對齊,即為32的倍數。 1.1修改位元組對齊: struct data{ int a; char b; char c; }__

13)結構問題

ngs dmi nts min mage ++ image http c語言 詳細可以看 臺式機的 C:\Documents and Settings\Administrator\桌面\C++基礎教程完整版視頻\01_C語言提高\d

結構

str center 但是 內存 read 地址 形式參數 class area 1 C語言裏可以在一個存儲區裏記錄多個相關數字這種存儲區的類型叫結構體類型,這種類型需要首先創建出來然後才能使用 2 結構體類型存儲區裏包含多個子存儲區,每個子存儲區可以記錄一個數字,結構體中

linux中結構【轉】

src double 無需 fine types 查看 真理 blog 多個 轉自:https://blog.csdn.net/suifengpiao_2011/article/details/47260085 linux中定義對齊字節 typedef struct

逆向基礎之結構

eof 成員對齊 偏移 str 最大 結構體 基礎 數據 sizeof 遵循以下原則,數據成員對齊;結構體大小;結構體有某些成員大,最大對齊,對齊參數筆結構體的sizeof小,偏移以此為準。struct{ char a;int b;char c;}a 1b,補3b 4bc

結構總結

結構體對齊 結構體對齊到底是什麼,看了網上很多的解答,彙總成個人經驗 什麼是結構體對齊 結構體對齊規則 考慮一個問題,為什麼要設計記憶體對齊的處理方式呢? ENDING 結構體對齊到底是什麼,看了網上很多

結構——結構記憶體佈局

在C語言中,可以通過#pragma pack(n)來指定結構體按n位元組對齊(這裡的n是2的較小整數次冪)。如果程式設計者不指定對齊位元組數,那麼預設的會按照結構體中最長那一項對齊,如在64位作業系統中,當結構體中出現(void *),(long)型別,則必然是按照8位元組對齊;當最大的是int,那麼就按照4

結構——結構體內存布局

聲明 amp pri 最大的 結構 sdn int spa turn 在C語言中,可以通過#pragma pack(n)來指定結構體按n字節對齊(這裏的n是2的較小整數次冪)。如果程序設計者不指定對齊字節數,那麽默認的會按照結構體中最長那一項對齊,如在64位操作系統中,當結

sizeof與strlen用法詳解(結構)

#include<stdio.h> int main(int argc,char **argv) { unsigned char a[10] ={1,2,1,2,3,4}; unsigned char a1[10] ={0,0,0,0,0,0}; char *

結構問題.

Intel、微軟等公司曾經出過一道類似的面試題: 2.1 自然對界   struct是一種複合資料型別,其構成元素既可以是基本資料型別(如int、long、float等)的變數,也可以是一些複合資料型別(如array、struct、union等)的資料單元。對於結構體,編譯器會自動進行成員變數的對齊,以提高

結構的原則及原因分析

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

64位系統結構訪問段錯誤

先放程式碼: #include struct s { char c; int b; }; int main(void){ printf("sizeof (struct s) = %d.\n

結構問題以及強制型別轉換問題總結

一、什麼是對齊     現在使用的計算機中記憶體空間都是按照位元組劃分的,從理論上講似乎對任何型別的變數的訪問都可以從任何地址開始,但實際上計算機系統對於基本資料型別在記憶體中的存放位置都有限制。舉個例子,一個變數佔用n個位元組,則該變數的起始地址必須能夠被n整除,即存放