當前位置:首頁 » 編程語言 » c語言中變數存儲內存對齊
擴展閱讀
webinf下怎麼引入js 2023-08-31 21:54:13
堡壘機怎麼打開web 2023-08-31 21:54:11

c語言中變數存儲內存對齊

發布時間: 2023-07-18 08:30:21

『壹』 c語言變數如何存儲在內存中

在系統為一個程序分配的空間中,分成許多段,比如有代碼段,存放程序可執行代碼,有數據段,可以分配變數,有常量段,專門存放常量,是只讀的。常量變數可以位於數據段中,仍然是變數,只是程序沒有改動許可權。在程序執行到main之前,系統負責將常量區存儲的常量賦值給常量。

『貳』 c++中內存是如何對齊的

有虛函數的話就有虛表,虛表保存虛函數地址,一個地址佔用的長度根據編譯器不同有可能不同,vs裡面是8個位元組,在devc++裡面是4個位元組。類和結構體的對齊方式相同,有兩條規則
1、數據成員對齊規則:結構(struct)(或聯合(union))的數據成員,第一個數據成員放在offset為0的地方,以後每個數據成員的對齊按照#pragma pack指定的數值和這個數據成員自身長度中,比較小的那個進行。
2、結構(或聯合)的整體對齊規則:在數據成員完成各自對齊之後,結構(或聯合)本身也要進行對齊,對齊將按照#pragma pack指定的數值和結構(或聯合)最大數據成員長度中,比較小的那個進行

下面是我收集的關於內存對齊的一篇很好的文章:

在最近的項目中,我們涉及到了「內存對齊」技術。對於大部分程序員來說,「內存對齊」對他們來說都應該是「透明的」。「內存對齊」應該是編譯器的 「管轄范圍」。編譯器為程序中的每個「數據單元」安排在適當的位置上。但是C語言的一個特點就是太靈活,太強大,它允許你干預「內存對齊」。如果你想了解更加底層的秘密,「內存對齊」對你就不應該再透明了。

一、內存對齊的原因
大部分的參考資料都是如是說的:
1、平台原因(移植原因):不是所有的硬體平台都能訪問任意地址上的任意數據的;某些硬體平台只能在某些地址處取某些特定類型的數據,否則拋出硬體異常。
2、性能原因:數據結構(尤其是棧)應該盡可能地在自然邊界上對齊。原因在於,為了訪問未對齊的內存,處理器需要作兩次內存訪問;而對齊的內存訪問僅需要一次訪問。

二、對齊規則
每個特定平台上的編譯器都有自己的默認「對齊系數」(也叫對齊模數)。程序員可以通過預編譯命令#pragma pack(n),n=1,2,4,8,16來改變這一系數,其中的n就是你要指定的「對齊系數」。

規則:
1、數據成員對齊規則:結構(struct)(或聯合(union))的數據成員,第一個數據成員放在offset為0的地方,以後每個數據成員的對齊按照#pragma pack指定的數值和這個數據成員自身長度中,比較小的那個進行。
2、結構(或聯合)的整體對齊規則:在數據成員完成各自對齊之後,結構(或聯合)本身也要進行對齊,對齊將按照#pragma pack指定的數值和結構(或聯合)最大數據成員長度中,比較小的那個進行。
3、結合1、2顆推斷:當#pragma pack的n值等於或超過所有數據成員長度的時候,這個n值的大小將不產生任何效果。

三、試驗
我們通過一系列例子的詳細說明來證明這個規則吧!
我試驗用的編譯器包括GCC 3.4.2和VC6.0的C編譯器,平台為Windows XP + Sp2。

我們將用典型的struct對齊來說明。首先我們定義一個struct:
#pragma pack(n) /* n = 1, 2, 4, 8, 16 */
struct test_t {
int a;
char b;
short c;
char d;
};
#pragma pack(n)
首先我們首先確認在試驗平台上的各個類型的size,經驗證兩個編譯器的輸出均為:
sizeof(char) = 1
sizeof(short) = 2
sizeof(int) = 4

我們的試驗過程如下:通過#pragma pack(n)改變「對齊系數」,然後察看sizeof(struct test_t)的值。

1、1位元組對齊(#pragma pack(1))
輸出結果:sizeof(struct test_t) = 8 [兩個編譯器輸出一致]
分析過程:
1) 成員數據對齊
#pragma pack(1)
struct test_t {
int a; /* 長度4 < 1 按1對齊;起始offset=0 0%1=0;存放位置區間[0,3] */
char b; /* 長度1 = 1 按1對齊;起始offset=4 4%1=0;存放位置區間[4] */
short c; /* 長度2 > 1 按1對齊;起始offset=5 5%1=0;存放位置區間[5,6] */
char d; /* 長度1 = 1 按1對齊;起始offset=7 7%1=0;存放位置區間[7] */
};
#pragma pack()
成員總大小=8

2) 整體對齊
整體對齊系數 = min((max(int,short,char), 1) = 1
整體大小(size)=$(成員總大小) 按 $(整體對齊系數) 圓整 = 8 /* 8%1=0 */ [注1]

2、2位元組對齊(#pragma pack(2))
輸出結果:sizeof(struct test_t) = 10 [兩個編譯器輸出一致]
分析過程:
1) 成員數據對齊
#pragma pack(2)
struct test_t {
int a; /* 長度4 > 2 按2對齊;起始offset=0 0%2=0;存放位置區間[0,3] */
char b; /* 長度1 < 2 按1對齊;起始offset=4 4%1=0;存放位置區間[4] */
short c; /* 長度2 = 2 按2對齊;起始offset=6 6%2=0;存放位置區間[6,7] */
char d; /* 長度1 < 2 按1對齊;起始offset=8 8%1=0;存放位置區間[8] */
};
#pragma pack()
成員總大小=9

2) 整體對齊
整體對齊系數 = min((max(int,short,char), 2) = 2
整體大小(size)=$(成員總大小) 按 $(整體對齊系數) 圓整 = 10 /* 10%2=0 */

3、4位元組對齊(#pragma pack(4))
輸出結果:sizeof(struct test_t) = 12 [兩個編譯器輸出一致]
分析過程:
1) 成員數據對齊
#pragma pack(4)
struct test_t {
int a; /* 長度4 = 4 按4對齊;起始offset=0 0%4=0;存放位置區間[0,3] */
char b; /* 長度1 < 4 按1對齊;起始offset=4 4%1=0;存放位置區間[4] */
short c; /* 長度2 < 4 按2對齊;起始offset=6 6%2=0;存放位置區間[6,7] */
char d; /* 長度1 < 4 按1對齊;起始offset=8 8%1=0;存放位置區間[8] */
};
#pragma pack()
成員總大小=9

2) 整體對齊
整體對齊系數 = min((max(int,short,char), 4) = 4
整體大小(size)=$(成員總大小) 按 $(整體對齊系數) 圓整 = 12 /* 12%4=0 */

4、8位元組對齊(#pragma pack(8))
輸出結果:sizeof(struct test_t) = 12 [兩個編譯器輸出一致]
分析過程:
1) 成員數據對齊
#pragma pack(8)
struct test_t {
int a; /* 長度4 < 8 按4對齊;起始offset=0 0%4=0;存放位置區間[0,3] */
char b; /* 長度1 < 8 按1對齊;起始offset=4 4%1=0;存放位置區間[4] */
short c; /* 長度2 < 8 按2對齊;起始offset=6 6%2=0;存放位置區間[6,7] */
char d; /* 長度1 < 8 按1對齊;起始offset=8 8%1=0;存放位置區間[8] */
};
#pragma pack()
成員總大小=9

2) 整體對齊
整體對齊系數 = min((max(int,short,char), 8) = 4
整體大小(size)=$(成員總大小) 按 $(整體對齊系數) 圓整 = 12 /* 12%4=0 */

5、16位元組對齊(#pragma pack(16))
輸出結果:sizeof(struct test_t) = 12 [兩個編譯器輸出一致]
分析過程:
1) 成員數據對齊
#pragma pack(16)
struct test_t {
int a; /* 長度4 < 16 按4對齊;起始offset=0 0%4=0;存放位置區間[0,3] */
char b; /* 長度1 < 16 按1對齊;起始offset=4 4%1=0;存放位置區間[4] */
short c; /* 長度2 < 16 按2對齊;起始offset=6 6%2=0;存放位置區間[6,7] */
char d; /* 長度1 < 16 按1對齊;起始offset=8 8%1=0;存放位置區間[8] */
};
#pragma pack()
成員總大小=9

2) 整體對齊
整體對齊系數 = min((max(int,short,char), 16) = 4
整體大小(size)=$(成員總大小) 按 $(整體對齊系數) 圓整 = 12 /* 12%4=0 */

四、結論
8位元組和16位元組對齊試驗證明了「規則」的第3點:「當#pragma pack的n值等於或超過所有數據成員長度的時候,這個n值的大小將不產生任何效果」。另外內存對齊是個很復雜的東西,上面所說的在有些時候也可能不正確。呵呵^_^

[注1]
什麼是「圓整」?
舉例說明:如上面的8位元組對齊中的「整體對齊」,整體大小=9 按 4 圓整 = 12
圓整的過程:從9開始每次加一,看是否能被4整除,這里9,10,11均不能被4整除,到12時可以,則圓整結束。

『叄』 C語言裡面的位元組對齊的計算方法能講解1下嗎

首先你要知道為什麼會出現位元組對齊,這是一種用空間換時間的做法,因為對齊之後可以提高取數的效率。結構體的大小一般是4或者8的倍數,具體是以最大的變數類型的大小為基數的。也就是說在內存中,數據一般是放在一個4的整數倍的起始地址。
sturct S
{
char c;
short s;
int i;
}
它的大小是8個位元組,因為前面兩個佔了4位元組。如果寫成下面這種形式,它就是12位元組了
struct S
{
char c;
int i;
short s;
}
因為為了提高存取效率,所以第一個成員變數佔了四個位元組的空間,最後一個成員也佔了四個位元組

你非要說計算方法的話要根據具體情況來算了,首先看結構體中哪一個變數所佔的位元組數最大,然後後面的對齊計算都以這個為基數(我這里以4位元組為例)。接著,你把每一個成員變數的大小依次相加(按照結構體定義中的順序,從第一個加到最後一個)。當你加到某一個變數的時候,發現超過了4個位元組,那麼就把這個變數之前的內容當作是一個整體,它們一共點4個位元組,後面的再繼續這個過程

『肆』 C語言有關內存對齊的問題

//比如有這樣的結構體
structA
{
chara;//佔1個位元組
intb;//佔4個位元組
};

//那這個結構體一共要佔5個位元組的內存嗎?
//不是的,系統會把它填充成8個位元組,這就是位元組對齊
//那系統為什麼要這樣做呢,這涉及到存取的效率問題
//從硬體角度看,32位的CPU通過匯流排訪問內存,一個匯流排周期訪問32位內存數據
//剛剛好4個位元組,所以4位元組對齊的存取效率是較高的
//這就是犧牲空間換取效率了
//當然不對齊CPU也能訪問,就是要花點功夫而已

『伍』 什麼是C語言結構體位元組對齊,為什麼要對齊

對齊跟數據在內存中的位置有關。如果一個變數的內存地址正好位於它長度的整數倍,他就被稱做自然對齊。比如在32位cpu下,假設一個整型變數的地址為0x00000004,那它就是自然對齊的。

需要位元組對齊的根本原因在於CPU訪問數據的效率問題。假設上面整型變數的地址不是自然對齊,比如為0x00000002,則CPU如果取它的值的話需要訪問兩次內存,第一次取從0x00000002-0x00000003的一個short,第二次取從0x00000004-0x00000005的一個short然後組合得到所要的數據,如果變數在0x00000003地址上的話則要訪問三次內存,第一次為char,第二次為short,第三次為char,然後組合得到整型數據。而如果變數在自然對齊位置上,則只要一次就可以取出數據。一些系統對對齊要求非常嚴格,比如sparc系統,如果取未對齊的數據會發生錯誤,舉個例:

char ch[8];
char *p = &ch[1];
int i = *(int *)p;

運行時會報segment error,而在x86上就不會出現錯誤,只是效率下降。

『陸』 C語言內存對齊問題.

為了有助於加快計算機的取數速度,編譯器默認會對結構體進行處理(實際上其它地方的數據變數也是如此),讓寬度為2的基本數據類型(short等)都位於能被2整除的地址上,讓寬度為4的基數據類型(int等)都位於能被4整除的地址上,以此類推。這樣,兩個數中間就可能需要加入填充位元組,所以整個結構體的sizeof值就增長了。


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

你這里struct的首地址要能被double的8位元組整除,

  1. char佔1位元組

  2. int要被此時的地址整除,那麼需要補上3位元組,所以這里一共該是8位元組

  3. short2位元組的,一共10位元組,不滿足3),不能整除int,故添加2位元組

  4. 最後是double 8位元組,這里地址應該是首地址+12不能被8整除,所以+4位元組。最後一共24B!

    問題解決求採納!

『柒』 c語言結構體對齊的問題。

這是個好問題!
為什麼會有對齊的問題呢?簡單的說就是為了提高訪問內存的效率,這和CPU內部的機制有關,如果你想深入理解,需要閱讀 Intel 開發者手冊。對齊採用的總體原則是這樣的:4位元組變數的存放要對齊到可以被4整除的地址上,8位元組變數的存放要對齊到可以被8整除的地址上。其他變數類推就行了。如果沒對齊編譯器就會將某個變數的存儲往後推遲幾個位元組,以保證對齊後再存放。
具體到這個問題就是可以先假設結構體變數從地址0處開始存放,那麼第一種情況就是這樣的了:
cat 存放的位置是地址0-地址3
a數組存放的位置是地址4-地址23
dog存放的位置是地址24到地址31
這里可以看到它們都符合對齊的原則(即每個變數開始存放的地址可以除盡它們所佔的位元組數),所以是32
第二種情況是這樣的:
cat仍然存放到地址0-地址3處
a數組是地址4到地址27處
存放dog時編譯器計算除下一個地址28並不能除盡double的位元組數8,於是它要將地址進行遞增。29,30,31仍然不能除盡8,知道遞增到32時可以將8除盡了,所以dog變數會被存放在地址32到地址39處,從地址0到地址39正好40個位元組,這就解釋了第二種情況了。

ps. 其實你這個問題還有一種變種就是一個結構體里在套一個結構體,這時會牽扯到內部的結構體對齊的問題。等你以後遇見了再給我提問吧,我給你解釋。

『捌』 在arm中c語言編程,定義的多位元組變數和結構體,最好使其為對齊存放."對齊存放"是

ARM支持16bit和32bit的地址訪問,即變數地址能夠被2或4整除,這時性能比較好,也便於移植。結構體的對齊就是指的結構體內部的每個成員變數地址盡可能對齊到2或4位元組位置,如定義為:
struct{
char ch1;
char ch2;
short ss;
int i;

}var;
字元變數ch1, ch2為位元組對齊,短整型變數ss為半字對齊,整型變數i為字對齊,結構體內的變數比較緊湊,且已自然對齊,結構體變數var佔用總空間為8個位元組。如果改為這樣:
struct{
char ch1;
short ss;
char ch2;
short ss;
int i;
}var;
var變數最終佔用的空間為12個位元組,存取這個結構體時比前面那個需要的時間要多,另外如果用pack(1)指定結構體位元組對齊的話,後面的方式移植到其它系統可能會出現死機問題。