1. 程式人生 > >C 深度剖析記憶體對齊

C 深度剖析記憶體對齊

首先說明一下,本文是轉載自:

引言

     考慮下面的結構體定義:

typedef struct{
    char  c1;
    short s; 
    char  c2; 
    int   i;
}T_FOO;

     假設這個結構體的成員在記憶體中是緊湊排列的,且c1的起始地址是0,則s的地址就是1,c2的地址是3,i的地址是4。

     現在,我們編寫一個簡單的程式:

複製程式碼
int main(void){  
    T_FOO a; 
    printf("c1 -> %d, s -> %d, c2 -> %d, i -> %d\n
", (unsigned int)(void*)&a.c1 - (unsigned int)(void*)&a, (unsigned int)(void*)&a.s - (unsigned int)(void*)&a, (unsigned int)(void*)&a.c2 - (unsigned int)(void*)&a, (unsigned int)(void*)&a.i - (unsigned int)(void*)&a);
return 0; }
複製程式碼

     執行後輸出: 

c1 -> 0, s -> 2, c2 -> 4, i -> 8

     為什麼會這樣?這就是位元組對齊導致的問題。

     本文在參考諸多資料的基礎上,詳細介紹常見的位元組對齊問題。因成文較早,資料來源大多已不可考,敬請諒解。

一  什麼是位元組對齊

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

二  對齊的原因和作用

     不同硬體平臺對儲存空間的處理上存在很大的不同。某些平臺對特定型別的資料只能從特定地址開始存取,而不允許其在記憶體中任意存放。例如Motorola 68000 處理器不允許16位的字存放在奇地址,否則會觸發異常,因此在這種架構下程式設計必須保證位元組對齊。

     但最常見的情況是,如果不按照平臺要求對資料存放進行對齊,會帶來存取效率上的損失。比如32位的Intel處理器通過匯流排訪問(包括讀和寫)記憶體資料。每個匯流排週期從偶地址開始訪問32位記憶體資料,記憶體資料以位元組為單位存放。如果一個32位的資料沒有存放在4位元組整除的記憶體地址處,那麼處理器就需要2個匯流排週期對其進行訪問,顯然訪問效率下降很多。

     因此,通過合理的記憶體對齊可以提高訪問效率。為使CPU能夠對資料進行快速訪問,資料的起始地址應具有“對齊”特性。比如4位元組資料的起始地址應位於4位元組邊界上,即起始地址能夠被4整除。

     此外,合理利用位元組對齊還可以有效地節省儲存空間。但要注意,在32位機中使用1位元組或2位元組對齊,反而會降低變數訪問速度。因此需要考慮處理器型別。還應考慮編譯器的型別。在VC/C++和GNU GCC中都是預設是4位元組對齊。

三  對齊的分類和準則

     主要基於Intel X86架構介紹結構體對齊和棧記憶體對齊,位域本質上為結構體型別。

     對於Intel X86平臺,每次分配記憶體應該是從4的整數倍地址開始分配,無論是對結構體變數還是簡單型別的變數。

3.1 結構體對齊

     在C語言中,結構體是種複合資料型別,其構成元素既可以是基本資料型別(如int、long、float等)的變數,也可以是一些複合資料型別(如陣列、結構體、聯合等)的資料單元。編譯器為結構體的每個成員按照其自然邊界(alignment)分配空間。各成員按照它們被宣告的順序在記憶體中順序儲存,第一個成員的地址和整個結構的地址相同。

     位元組對齊的問題主要就是針對結構體。

3.1.1 簡單示例

     先看個簡單的例子(32位,X86處理器,GCC編譯器):

    【例1】設結構體如下定義:

複製程式碼
struct A{
    int    a;
    char   b;
    short  c;
};
struct B{
    char   b;
    int    a;
    short  c;
};
複製程式碼

     已知32位機器上各資料型別的長度為:char為1位元組、short為2位元組、int為4位元組、long為4位元組、float為4位元組、double為8位元組。那麼上面兩個結構體大小如何呢?

     結果是:sizeof(strcut A)值為8;sizeof(struct B)的值卻是12。 

     結構體A中包含一個4位元組的int資料,一個1位元組char資料和一個2位元組short資料;B也一樣。按理說A和B大小應該都是7位元組。之所以出現上述結果,就是因為編譯器要對資料成員在空間上進行對齊。

3.1.2 對齊準則

     先來看四個重要的基本概念:

     1) 資料型別自身的對齊值:char型資料自身對齊值為1位元組,short型資料為2位元組,int/float型為4位元組,double型為8位元組。

     2) 結構體或類的自身對齊值:其成員中自身對齊值最大的那個值。

     3) 指定對齊值:#pragma pack (value)時的指定對齊值value。

     4) 資料成員、結構體和類的有效對齊值:自身對齊值和指定對齊值中較小者,即有效對齊值=min{自身對齊值,當前指定的pack值}

     基於上面這些值,就可以方便地討論具體資料結構的成員和其自身的對齊方式。

     其中,有效對齊值N是最終用來決定資料存放地址方式的值。有效對齊N表示“對齊在N上”,即該資料的“存放起始地址%N=0”。而資料結構中的資料變數都是按定義的先後順序存放。第一個資料變數的起始地址就是資料結構的起始地址。結構體的成員變數要對齊存放,結構體本身也要根據自身的有效對齊值圓整(即結構體成員變數佔用總長度為結構體有效對齊值的整數倍)

     以此分析3.1.1節中的結構體B:

     假設B從地址空間0x0000開始存放,且指定對齊值預設為4(4位元組對齊)。成員變數b的自身對齊值是1,比預設指定對齊值4小,所以其有效對齊值為1,其存放地址0x0000符合0x0000%1=0。成員變數a自身對齊值為4,所以有效對齊值也為4,只能存放在起始地址為0x0004~0x0007四個連續的位元組空間中,符合0x0004%4=0且緊靠第一個變數。變數c自身對齊值為 2,所以有效對齊值也是2,可存放在0x0008~0x0009兩個位元組空間中,符合0x0008%2=0。所以從0x0000~0x0009存放的都是B內容。

     再看資料結構B的自身對齊值為其變數中最大對齊值(這裡是b)所以就是4,所以結構體的有效對齊值也是4。根據結構體圓整的要求, 0x0000~0x0009=10位元組,(10+2)%4=0。所以0x0000A~0x000B也為結構體B所佔用。故B從0x0000到0x000B 共有12個位元組,sizeof(struct B)=12。

     之所以編譯器在後面補充2個位元組,是為了實現結構陣列的存取效率。試想如果定義一個結構B的陣列,那麼第一個結構起始地址是0沒有問題,但是第二個結構呢?按照陣列的定義,陣列中所有元素都緊挨著。如果我們不把結構體大小補充為4的整數倍,那麼下一個結構的起始地址將是0x0000A,這顯然不能滿足結構的地址對齊。因此要把結構體補充成有效對齊大小的整數倍。其實對於char/short/int/float/double等已有型別的自身對齊值也是基於陣列考慮的,只是因為這些型別的長度已知,所以他們的自身對齊值也就已知。 

     上面的概念非常便於理解,不過個人還是更喜歡下面的對齊準則。

     結構體位元組對齊的細節和具體編譯器實現相關,但一般而言滿足三個準則:

     1) 結構體變數的首地址能夠被其最寬基本型別成員的大小所整除;

     2) 結構體每個成員相對結構體首地址的偏移量(offset)都是成員大小的整數倍,如有需要編譯器會在成員之間加上填充位元組(internal adding);

     3) 結構體的總大小為結構體最寬基本型別成員大小的整數倍,如有需要編譯器會在最末一個成員之後加上填充位元組{trailing padding}。

     對於以上規則的說明如下:

     第一條:編譯器在給結構體開闢空間時,首先找到結構體中最寬的基本資料型別,然後尋找記憶體地址能被該基本資料型別所整除的位置,作為結構體的首地址。將這個最寬的基本資料型別的大小作為上面介紹的對齊模數。

     第二條:為結構體的一個成員開闢空間之前,編譯器首先檢查預開闢空間的首地址相對於結構體首地址的偏移是否是本成員大小的整數倍,若是,則存放本成員,反之,則在本成員和上一個成員之間填充一定的位元組,以達到整數倍的要求,也就是將預開闢空間的首地址後移幾個位元組。

     第三條:結構體總大小是包括填充位元組,最後一個成員滿足上面兩條以外,還必須滿足第三條,否則就必須在最後填充幾個位元組以達到本條要求。

    【例2】假設4位元組對齊,以下程式的輸出結果是多少?

複製程式碼
/* OFFSET巨集定義可取得指定結構體某成員在結構體內部的偏移 */
#define OFFSET(st, field)     (size_t)&(((st*)0)->field)
typedef struct{
    char  a;
    short b;
    char  c;
    int   d;
    char  e[3];
}T_Test;

int main(void){  
    printf("Size = %d\n  a-%d, b-%d, c-%d, d-%d\n  e[0]-%d, e[1]-%d, e[2]-%d\n",
           sizeof(T_Test), OFFSET(T_Test, a), OFFSET(T_Test, b),
           OFFSET(T_Test, c), OFFSET(T_Test, d), OFFSET(T_Test, e[0]),
           OFFSET(T_Test, e[1]),OFFSET(T_Test, e[2]));
    return 0;
}
複製程式碼

     執行後輸出如下:

Size = 16
    a-0, b-2, c-4, d-8
    e[0]-12, e[1]-13, e[2]-14

     下面來具體分析:

     首先char a佔用1個位元組,沒問題。

     short b本身佔用2個位元組,根據上面準則2,需要在b和a之間填充1個位元組。

     char c佔用1個位元組,沒問題。

     int d本身佔用4個位元組,根據準則2,需要在d和c之間填充3個位元組。

     char e[3];本身佔用3個位元組,根據原則3,需要在其後補充1個位元組。

     因此,sizeof(T_Test) = 1 + 1 + 2 + 1 + 3 + 4 + 3 + 1 = 16位元組。

3.1.3 對齊的隱患

3.1.3.1 資料型別轉換

     程式碼中關於對齊的隱患,很多是隱式的。例如,在強制型別轉換的時候:

複製程式碼
int main(void){  
    unsigned int i = 0x12345678;
        
    unsigned char *p = (unsigned char *)&i;
    *p = 0x00;
    unsigned short *p1 = (unsigned short *)(p+1);
    *p1 = 0x0000;

    return 0;
}
複製程式碼

     最後兩句程式碼,從奇數邊界去訪問unsigned short型變數,顯然不符合對齊的規定。在X86上,類似的操作只會影響效率;但在MIPS或者SPARC上可能導致error,因為它們要求必須位元組對齊。

     又如對於3.1.1節的結構體struct B,定義如下函式:

void Func(struct B *p){
    //Code
}

     在函式體內如果直接訪問p->a,則很可能會異常。因為MIPS認為a是int,其地址應該是4的倍數,但p->a的地址很可能不是4的倍數。

     如果p的地址不在對齊邊界上就可能出問題,比如p來自一個跨CPU的資料包(多種資料型別的資料被按順序放置在一個數據包中傳輸),或p是經過指標移位算出來的。因此要特別注意跨CPU資料的介面函式對介面輸入資料的處理,以及指標移位再強制轉換為結構指標進行訪問時的安全性。 

     解決方式如下:

     1) 定義一個此結構的區域性變數,用memmove方式將資料拷貝進來。

void Func(struct B *p){
    struct B tData;
    memmove(&tData, p, sizeof(struct B));
    //此後可安全訪問tData.a,因為編譯器已將tData分配在正確的起始地址上
}

     注意:如果能確定p的起始地址沒問題,則不需要這麼處理;如果不能確定(比如跨CPU輸入資料、或指標移位運算出來的資料要特別小心),則需要這樣處理。

     2) 用#pragma pack (1)將STRUCT_T定義為1位元組對齊方式。

3.1.3.2 處理器間資料通訊

     處理器間通過訊息(對於C/C++而言就是結構體)進行通訊時,需要注意位元組對齊以及位元組序的問題。

     大多數編譯器提供記憶體對其的選項供使用者使用。這樣使用者可以根據處理器的情況選擇不同的位元組對齊方式。例如C/C++編譯器提供的#pragma pack(n) n=1,2,4等,讓編譯器在生成目標檔案時,使記憶體資料按照指定的方式排布在1,2,4等位元組整除的記憶體地址處。

     然而在不同編譯平臺或處理器上,位元組對齊會造成訊息結構長度的變化。編譯器為了使位元組對齊可能會對訊息結構體進行填充,不同編譯平臺可能填充為不同的形式,大大增加處理器間資料通訊的風險。 

     下面以32位處理器為例,提出一種記憶體對齊方法以解決上述問題。

     對於本地使用的資料結構,為提高記憶體訪問效率,採用四位元組對齊方式;同時為了減少記憶體的開銷,合理安排結構體成員的位置,減少四位元組對齊導致的成員之間的空隙,降低記憶體開銷。

     對於處理器之間的資料結構,需要保證訊息長度不會因不同編譯平臺或處理器而導致訊息結構體長度發生變化,使用一位元組對齊方式對訊息結構進行緊縮;為保證處理器之間的訊息資料結構的記憶體訪問效率,採用位元組填充的方式自己對訊息中成員進行四位元組對齊。

     資料結構的成員位置要兼顧成員之間的關係、資料訪問效率和空間利用率。順序安排原則是:四位元組的放在最前面,兩位元組的緊接最後一個四位元組成員,一位元組緊接最後一個兩位元組成員,填充位元組放在最後。

     舉例如下:

複製程式碼
typedef struct tag_T_MSG{
    long  ParaA;
    long  ParaB;
    short ParaC;
    char  ParaD;
    char  Pad;   //填充位元組
}T_MSG;
複製程式碼

3.1.3.3 排查對齊問題

     如果出現對齊或者賦值問題可檢視:

     1) 編譯器的位元組序大小端設定;

     2) 處理器架構本身是否支援非對齊訪問;

     3) 如果支援看設定對齊與否,如果沒有則看訪問時需要加某些特殊的修飾來標誌其特殊訪問操作。 

3.1.4 更改對齊方式

     主要是更改C編譯器的預設位元組對齊方式。   

     在預設情況下,C編譯器為每一個變數或是資料單元按其自然對界條件分配空間。一般地,可以通過下面的方法來改變預設的對界條件:

  • 使用偽指令#pragma pack(n):C編譯器將按照n個位元組對齊;
  • 使用偽指令#pragma pack(): 取消自定義位元組對齊方式。

     另外,還有如下的一種方式(GCC特有語法):

  • __attribute((aligned (n))): 讓所作用的結構成員對齊在n位元組自然邊界上。如果結構體中有成員的長度大於n,則按照最大成員的長度來對齊。
  • __attribute__ ((packed)): 取消結構在編譯過程中的優化對齊,按照實際佔用位元組數進行對齊。

    【注】__attribute__機制是GCC的一大特色,可以設定函式屬性(Function Attribute)、變數屬性(Variable Attribute)和型別屬性(Type Attribute)。詳細介紹請參考:

     下面具體針對MS VC/C++ 6.0編譯器介紹下如何修改編譯器預設對齊值。

     1) VC/C++ IDE環境中,可在[Project]|[Settings],C/C++選項卡Category的Code Generation選項的Struct Member Alignment中修改,預設是8位元組。

 

     VC/C++中的編譯選項有/Zp[1|2|4|8|16],/Zpn表示以n位元組邊界對齊。n位元組邊界對齊是指一個成員的地址必須安排在成員的尺寸的整數倍地址上或者是n的整數倍地址上,取它們中的最小值。亦即:min(sizeof(member), n)。

     實際上,1位元組邊界對齊也就表示結構成員之間沒有空洞。

     /Zpn選項應用於整個工程,影響所有參與編譯的結構體。在Struct member alignment中可選擇不同的對齊值來改變編譯選項。

     2) 在編碼時,可用#pragma pack動態修改對齊值。具體語法說明見附錄5.3節。

     自定義對齊值後要用#pragma pack()來還原,否則會對後面的結構造成影響。 

    【例3】分析如下結構體C:

複製程式碼
#pragma pack(2)  //指定按2位元組對齊
struct C{
    char  b;
    int   a;
    short c;
};
#pragma pack()   //取消指定對齊,恢復預設對齊
複製程式碼

     變數b自身對齊值為1,指定對齊值為2,所以有效對齊值為1,假設C從0x0000開始,則b存放在0x0000,符合0x0000%1= 0;變數a自身對齊值為4,指定對齊值為2,所以有效對齊值為2,順序存放在0x0002~0x0005四個連續位元組中,符合0x0002%2=0。變數c的自身對齊值為2,所以有效對齊值為2,順序存放在0x0006~0x0007中,符合 0x0006%2=0。所以從0x0000到0x00007共八位元組存放的是C的變數。C的自身對齊值為4,所以其有效對齊值為2。又8%2=0,C只佔用0x0000~0x0007的八個位元組。所以sizeof(struct C) = 8。

     注意,結構體對齊到的位元組數並非完全取決於當前指定的pack值,如下:

複製程式碼
#pragma pack(8)
struct D{
    char  b;
    short a;
    char  c;
};
#pragma pack()
複製程式碼

     雖然#pragma pack(8),但依然按照兩位元組對齊,所以sizeof(struct D)的值為6。因為:對齊到的位元組數 = min{當前指定的pack值,最大成員大小}。

     另外,GNU GCC編譯器中按1位元組對齊可寫為以下形式:

#define GNUC_PACKED __attribute__((packed))
struct C{
    char  b;
    int   a;
    short c;
}GNUC_PACKED;

     此時sizeof(struct C)的值為7。

3.2 棧記憶體對齊

     在VC/C++中,棧的對齊方式不受結構體成員對齊選項的影響。總是保持對齊且對齊在4位元組邊界上。

    【例4】

複製程式碼
#pragma pack(push, 1)  //後面可改為1, 2, 4, 8
struct StrtE{
    char m1;
    long m2;
};
#pragma pack(pop)

int main(void){  
    char a;
    short b;
    int c;
    double d[2];
    struct StrtE s;
        
    printf("a    address:   %p\n", &a);
    printf("b    address:   %p\n", &b);
    printf("c    address:   %p\n", &c);
    printf("d[0] address:   %p\n", &(d[0]));
    printf("d[1] address:   %p\n", &(d[1]));
    printf("s    address:   %p\n", &s);
    printf("s.m2 address:   %p\n", &(s.m2));
    return 0;
}
複製程式碼

     結果如下:

複製程式碼
a    address:   0xbfc4cfff
b    address:   0xbfc4cffc
c    address:   0xbfc4cff8
d[0] address:   0xbfc4cfe8
d[1] address:   0xbfc4cff0
s    address:   0xbfc4cfe3
s.m2 address:   0xbfc4cfe4
複製程式碼

     可以看出都是對齊到4位元組。並且前面的char和short並沒有被湊在一起(成4位元組),這和結構體內的處理是不同的。

     至於為什麼輸出的地址值是變小的,這是因為該平臺下的棧是倒著“生長”的。

3.3 位域對齊

3.3.1 位域定義

     有些資訊在儲存時,並不需要佔用一個完整的位元組,而只需佔幾個或一個二進位制位。例如在存放一個開關量時,只有0和1兩種狀態,用一位二進位即可。為了節省儲存空間和處理簡便,C語言提供了一種資料結構,稱為“位域”或“位段”。

     位域是一種特殊的結構成員或聯合成員(即只能用在結構或聯合中),用於指定該成員在記憶體儲存時所佔用的位數,從而在機器內更緊湊地表示資料。每個位域有一個域名,允許在程式中按域名操作對應的位。這樣就可用一個位元組的二進位制位域來表示幾個不同的物件。

     位域定義與結構定義類似,其形式為:

struct 位域結構名

       { 位域列表 };

     其中位域列表的形式為:

型別說明符位域名:位域長度

     位域的使用和結構成員的使用相同,其一般形式為:

位域變數名.位域名

     位域允許用各種格式輸出。

     位域在本質上就是一種結構型別,不過其成員是按二進位分配的。位域變數的說明與結構變數說明的方式相同,可先定義後說明、同時定義說明或直接說明。      

     位域的使用主要為下面兩種情況:

     1) 當機器可用記憶體空間較少而使用位域可大量節省記憶體時。如把結構作為大陣列的元素時。

     2) 當需要把一結構體或聯合對映成某預定的組織結構時。如需要訪問位元組內的特定位時。

3.3.2 對齊準則

     位域成員不能單獨被取sizeof值。下面主要討論含有位域的結構體的sizeof。 

     C99規定int、unsigned int和bool可以作為位域型別,但編譯器幾乎都對此作了擴充套件,允許其它型別的存在。位域作為嵌入式系統中非常常見的一種程式設計工具,優點在於壓縮程式的儲存空間。

     其對齊規則大致為:

     1) 如果相鄰位域欄位的型別相同,且其位寬之和小於型別的sizeof大小,則後面的欄位將緊鄰前一個欄位儲存,直到不能容納為止;

     2) 如果相鄰位域欄位的型別相同,但其位寬之和大於型別的sizeof大小,則後面的欄位將從新的儲存單元開始,其偏移量為其型別大小的整數倍;

     3) 如果相鄰的位域欄位的型別不同,則各編譯器的具體實現有差異,VC6採取不壓縮方式,Dev-C++和GCC採取壓縮方式;

     4) 如果位域欄位之間穿插著非位域欄位,則不進行壓縮;

     5) 整個結構體的總大小為最寬基本型別成員大小的整數倍,而位域則按照其最寬型別位元組數對齊。

    【例5】

struct BitField{
    char element1  : 1;
    char element2  : 4;
    char element3  : 5;
};

     位域型別為char,第1個位元組僅能容納下element1和element2,所以element1和element2被壓縮到第1個位元組中,而element3只能從下一個位元組開始。因此sizeof(BitField)的結果為2。

    【例6】

struct BitField1{
    char element1   : 1;
    short element2  : 5;
    char element3   : 7;
};

     由於相鄰位域型別不同,在VC6中其sizeof為6,在Dev-C++中為2。

    【例7】

struct BitField2{
    char element1  : 3;
    char element2  ;
    char element3  : 5;
};

     非位域欄位穿插在其中,不會產生壓縮,在VC6和Dev-C++中得到的大小均為3。

    【例8】

複製程式碼
            
           

相關推薦

C 深度剖析記憶體

首先說明一下,本文是轉載自: 引言      考慮下面的結構體定義: typedef struct{ char c1; short s; char c2; int i; }T_FOO;      假設這個結構體的成員在記憶體中是緊湊排列的,且

c++結構體記憶體原理和方法

假設我們同時宣告兩個變數:  char a;  short b;  用&(取地址符號)觀察變數a,  b的地址的話,我們會發現(以16位CPU為例):  如果a的地址是0x0000,那麼b的地址將會是0x0002或者是0x0004。  那麼就出現這樣一個問題:0x0001這個地址沒有被使用,那它幹

結構體深度剖析記憶體引數,偏移量)

一、瞭解結構體 1 在C語言中,除了最常見用資料型別,字元型別(char)、整數型別(short、int、long )、實型(float、double),,,,,,最常見也是最經典的還有一種資料型別,那就是結構體。  二、結構體經典面試題:  (1)、什麼是結構體? 

C++虛擬函式表以及記憶體文章

C++虛擬函式表以及記憶體對齊文章 C++ 物件的記憶體佈局(上) https://blog.csdn.net/haoel/article/details/3081328 C++ 物件的記憶體佈局(下) https://blog.csdn.net/haoel/article/deta

C++11 記憶體 alignof alignas

一 現象 先看一段程式碼: struct s1 { char s; int i; }; struct s2 { int i; double d; }; cout << "-------basic type" << endl; c

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

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

C語言查缺補漏(七)結構體記憶體原則

忽略點七:結構體記憶體對齊原則 ​ 直到前幾個星期做了一道選擇題才知道,結構體元素的宣告順序可能影響結構體使用時所需的記憶體大小!!! ​ 一查才知道,在C語言中結構體有記憶體對齊原則,這個原則可以總結為兩點: ——資料成員對齊規則: ​ 結構體或聯合體的資料

C/C++ 記憶體原則及作用

struct/class/union記憶體對齊原則有四個: 1).資料成員對齊規則:結構(struct)(或聯合(union))的資料成員,第一個資料成員放在offset為0的地方,以後每個資料成員儲存的起始位置要從該成員大小或者成員的子成員大小(只要該成員有子成員,比

c++記憶體的問題

#include <iostream> typedef class { public: struct { int b; int c; }a; struct { bool e; char f; }d;

C語言中的struct——記憶體

sizeof計算結構體大小的時候具體是怎樣計算的 記憶體對齊的原則是根據最寬資料型別的大小進行對齊的 struct A { char a; 1+1 short b; 2 int c; 4 }; 大小是8個位元組 要進行記憶體對齊,是

關於C語言記憶體,提高定址效率問題

前言: 計算機的記憶體都是以位元組為單位來劃分的,CPU一般都是通過地址匯流排來訪問記憶體的,一次能處理幾個位元組,就命令地址匯流排去訪問幾個位元組,32位的CPU一次能處理4個位元組,就命令地址匯流排一次讀取4個位元組,讀少了浪費主頻,讀多了也處理不了。64位的CPU一般

C++中什麼是記憶體

以下資料是我從別人的文章抽取出來的,我認為比較有利於理解。加上一點我的理解     接下來我們好好討論一下記憶體對齊的作用   1.平臺原因(移植原因):不是所有的硬體平臺都能訪問任意地址上的任意資料,某些硬體平臺只能在某些地址處取某些特定型別的資料,否則丟擲硬體異常

【易錯】C語言結構體記憶體問題

對於一個結構體的位元組數大家有沒有遇到什麼疑問呢? 先看一個結構體: typedef struct Test { char a1; int a2; char a3; short a4; }Test_T; 在32位編譯系統下這一個結構體的位元組數是多少呢?是1+4

c語言記憶體與#pragma pack(n)

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

C進階之:記憶體

什麼是記憶體對齊?  記憶體對齊可以用一句話來概括:  “資料項只能儲存在地址是資料項大小的整數倍的記憶體位置上”。例如int型別佔用4個位元組,地址只能在0,4,8等位置上。  不同型別的資料在記憶體中按照一定的規則排列,而不一定是順序的一個接一個的排列,這就是所謂的

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

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

C可變引數的應用和記憶體

       可變引數提供了一個引數可供多種選擇        記憶體對齊可以最大限度保證速度,當然是在犧牲記憶體的前提下        1.  szieof

C語言之struct大小、首地址與記憶體—由結構體成員地址得到結構體首地址

被問到如下問題:給定一個結構體中某個變數地址,可否得到結構體變數的地址? 答案是可以,但是對不同的場合有不同的結果;這與微處理器平臺、編譯器的處理不可分割。 首先,對於處理器,大尾端、小尾端的因素必須考慮; 其次: 一、 ANSIC標準中並沒有規定,相鄰宣告的變數在記憶體中一定要相鄰。 為了程式的高效性,

C語言】結構體、聯合,記憶體規則總結

一、結構體 1.1什麼是結構體       在C語言中,結構體是一種資料結構,是C提供的聚合型別(C提供了兩種聚合型別:陣列和結構)的一種。結構體與陣列的區別是:陣列是相同型別的集合,而結構體可能具有不同的型別。 結構體也可以被宣告為變數,陣列或者指標等,用以實現較複雜的

Android平臺,C/C++程式碼記憶體問題(signal SIGBUS Error)

最近手機版本老出現崩潰,之前出現過,但很偶然。最近出現機率比較高,就跟查一下。 報了signal SIGBUS BUS Error,最終定位在uint32_t i32 = *((uint32_t*)m_data); 這句語出了問題, 確認m_data記憶體是正確的,並且在P