本文目錄一覽:
- 1、c語言 結構體位域問題
- 2、C語言結構體長度位元組對齊問題
- 3、關於C語言中的結構體位元組對齊
- 4、c語言結構體對齊的問題。
- 5、C語言中位域和結構體得區別是什麼?
- 6、什麼是C語言結構體位元組對齊,為什麼要對齊
c語言 結構體位域問題
c存在第三個位元組
sizeof結構體,這個要看結構體內變數是如何定義的,結構體存放數據有個對齊原則,找到佔用最大位元組的變數,然後都向它對齊,比如bool和char類型佔用一個位元組,short佔兩個位元組,int,float為4個位元組,double為八個位元組。
定義的順序不同,sizeof的結果不同。我給你舉個例子。
struct A{
int a;
char b;
char c;
};
sizeof(A)應該為4+1+1,但是需要對齊,所以這個值就是 8
圖1
這個是struct A的變數存儲
struct B{
char a;
int b;
char c;
};
sizeof(B) 這個值就是12
圖2
這個是struct B的變數存儲
C語言結構體長度位元組對齊問題
因為當結構體中有多個數據類型時,結構體的長度對齊是按數據類型長度最長的那個來對齊的,double類型佔8個位元組,所以每個成員變數都按8個位元組的長度來算,就是5*8=40,驗證程序如下:
#include stdio.h
struct chji
{
char name[9];
int number;
char sex;
double score;
float aa;
};
struct chji stu;
int main()
{
printf(“sizeof(struct chji)=%d\n”,sizeof(struct chji));
return 0;
}
運行結果:sizeof(struct chji)=40
如果要按單個位元組的長度來對齊的話,代碼如下:
#include stdio.h
#pragma pack(1)
struct chji
{
char name[9]; //9
int number; //4
char sex; //1
double score; //8
float aa; //4
};
struct chji stu;
#pragma pack()
int main()
{
printf(“sizeof(struct chji)=%d\n”,sizeof(struct chji));
return 0;
}
運行結果:sizeof(struct chji)=26
即9+4+1+8+4=26,你可以查下#pragma pack()相關的資料的,就會清楚了。
關於C語言中的結構體位元組對齊
仔細看一下書中的說明吧,這三言兩語介紹起來有點累
或從網上查閱相關的技術資料,有詳細描述。
一般簡單來說,結構體從第一個變數開始檢查空間的「對齊位元組數」,
默認以第一個位元組大小作為對齊位元組數,如果遇上的下一個位元組與當前對齊位元組數不同時,就按兩者中較大的來進行結構體空間分配,接下來的對齊就全按此值來對齊,直到再遇上不同的才進行檢查或改變。
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. 其實你這個問題還有一種變種就是一個結構體里在套一個結構體,這時會牽扯到內部的結構體對齊的問題。等你以後遇見了再給我提問吧,我給你解釋。
C語言中位域和結構體得區別是什麼?
C語言結構體對齊也是老生常談的話題了。基本上是面試題的必考題。內容雖然很基礎,但一不小心就會弄錯。寫出一個struct,然後sizeof,你會不會經常對結果感到奇怪?sizeof的結果往往都比你聲明的變數總長度要大,這是怎麼回事呢?
開始學的時候,也被此類問題困擾很久。其實相關的文章很多,感覺說清楚的不多。結構體到底怎樣對齊?
有人給對齊原則做過總結,具體在哪裡看到現在已記不起來,這裡引用一下前人的經驗(在沒有#pragma pack宏的情況下):
原則1、數據成員對齊規則:結構(struct或聯合union)的數據成員,第一個數據成員放在offset為0的地方,以後每個數據成員存儲的起始位置要從該成員大小的整數倍開始(比如int在32位機為4位元組,則要從4的整數倍地址開始存儲)。
原則2、結構體作為成員:如果一個結構里有某些結構體成員,則結構體成員要從其內部最大元素大小的整數倍地址開始存儲。(struct a里存有struct b,b里有char,int,double等元素,那b應該從8的整數倍開始存儲。)
原則3、收尾工作:結構體的總大小,也就是sizeof的結果,必須是其內部最大成員的整數倍,不足的要補齊。
這三個原則具體怎樣理解呢?我們看下面幾個例子,通過實例來加深理解。
例1:struct {
short a1;
short a2;
short a3;
}A;
struct{
long a1;
short a2;
}B;
sizeof(A) = 6; 這個很好理解,三個short都為2。
sizeof(B) = 8; 這個比是不是比預想的大2個位元組?long為4,short為2,整個為8,因為原則3。
例2:struct A{
int a;
char b;
short c;
};
struct B{
char b;
int a;
short c;
};
sizeof(A) = 8; int為4,char為1,short為2,這裡用到了原則1和原則3。
sizeof(B) = 12; 是否超出預想範圍?char為1,int為4,short為2,怎麼會是12?還是原則1和原則3。
深究一下,為什麼是這樣,我們可以看看內存里的布局情況。
a b c
A的內存布局:1111, 1*, 11
b a c
B的內存布局:1***, 1111, 11**
其中星號*表示填充的位元組。A中,b後面為何要補充一個位元組?因為c為short,其起始位置要為2的倍數,就是原則1。c的後面沒有補充,因為b和c正好佔用4個位元組,整個A佔用空間為4的倍數,也就是最大成員int類型的倍數,所以不用補充。
B中,b是char為1,b後面補充了3個位元組,因為a是int為4,根據原則1,起始位置要為4的倍數,所以b後面要補充3個位元組。c後面補充兩個位元組,根據原則3,整個B佔用空間要為4的倍數,c後面不補充,整個B的空間為10,不符,所以要補充2個位元組。
再看一個結構中含有結構成員的例子:
例3:struct A{
int a;
double b;
float c;
};
struct B{
char e[2];
int f;
double g;
short h;
struct A i;
};
sizeof(A) = 24; 這個比較好理解,int為4,double為8,float為4,總長為8的倍數,補齊,所以整個A為24。
sizeof(B) = 48; 看看B的內存布局。
e f g h i
B的內存布局:11* *, 1111, 11111111, 11 * * * * * *, 1111* * * *, 11111111, 1111 * * * *
i其實就是A的內存布局。i的起始位置要為24的倍數,所以h後面要補齊。把B的內存布局弄清楚,有關結構體的對齊方式基本就算掌握了。
以上講的都是沒有#pragma pack宏的情況,如果有#pragma pack宏,對齊方式按照宏的定義來。比如上面的結構體前加#pragma pack(1),內存的布局就會完全改變。sizeof(A) = 16; sizeof(B) = 32;
有了#pragma pack(1),內存不會再遵循原則1和原則3了,按1位元組對齊。沒錯,這不是理想中的沒有內存對齊的世界嗎。
a b c
A的內存布局:1111, 11111111, 1111
e f g h i
B的內存布局:11, 1111, 11111111, 11 , 1111, 11111111, 1111
那#pragma pack(2)的結果又是多少呢?#pragma pack(4)呢?留給大家自己思考吧,相信沒有問題。
還有一種常見的情況,結構體中含位域欄位。位域成員不能單獨被取sizeof值。C99規定int、unsigned int和bool可以作為位域類型,但編譯器幾乎都對此作了擴展,允許其它類型類型的存在。
使用位域的主要目的是壓縮存儲,其大致規則為:
1) 如果相鄰位域欄位的類型相同,且其位寬之和小於類型的sizeof大小,則後面的欄位將緊鄰前一個欄位存儲,直到不能容納為止;
2) 如果相鄰位域欄位的類型相同,但其位寬之和大於類型的sizeof大小,則後面的欄位將從新的存儲單元開始,其偏移量為其類型大小的整數倍;
3) 如果相鄰的位域欄位的類型不同,則各編譯器的具體實現有差異,VC6採取不壓縮方式,Dev-C++採取壓縮方式;
4) 如果位域欄位之間穿插著非位域欄位,則不進行壓縮;
5) 整個結構體的總大小為最寬基本類型成員大小的整數倍。
還是讓我們來看看例子。
例4:struct A{
char f1 : 3;
char f2 : 4;
char f3 : 5;
};
a b c
A的內存布局:111, 1111 *, 11111 * * *
位域類型為char,第1個位元組僅能容納下f1和f2,所以f2被壓縮到第1個位元組中,而f3隻能從下一個位元組開始。因此sizeof(A)的結果為2。
例5:struct B{
char f1 : 3;
short f2 : 4;
char f3 : 5;
};
由於相鄰位域類型不同,在VC6中其sizeof為6,在Dev-C++中為2。
例6:struct C{
char f1 : 3;
char f2;
char f3 : 5;
};
非位域欄位穿插在其中,不會產生壓縮,在VC6和Dev-C++中得到的大小均為3。
考慮一個問題,為什麼要設計內存對齊的處理方式呢?如果體系結構是不對齊的,成員將會一個挨一個存儲,顯然對齊更浪費了空間。那麼為什麼要使用對齊呢?體系結構的對齊和不對齊,是在時間和空間上的一個權衡。對齊節省了時間。假設一個體系結構的字長為w,那麼它同時就假設了在這種體系結構上對寬度為w的數據的處理最頻繁也是最重要的。它的設計也是從優先提高對w位數據操作的效率來考慮的。有興趣的可以google一下,人家就可以跟你解釋的,一大堆的道理。
最後順便提一點,在設計結構體的時候,一般會尊照一個習慣,就是把佔用空間小的類型排在前面,佔用空間大的類型排在後面,這樣可以相對節約一些對齊空間。
本篇文章來源於:開發學院 原文鏈接:
什麼是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上就不會出現錯誤,只是效率下降。
原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-tw/n/237592.html