C語言結(jié)構(gòu)體中內(nèi)存對齊的問題理解
前言
學(xué)C的同學(xué)應(yīng)該知道~
想精通C語言就不得不面對—指針與內(nèi)存
續(xù)上次指針的進階,這一章我來聊一聊C語言內(nèi)存對齊的問題
學(xué)習(xí)結(jié)構(gòu)體的你有沒有注意過結(jié)構(gòu)體向系統(tǒng)申請的內(nèi)存為多少呢的??
思考
#include<stdio.h> typedef struct s1 { char a; char b; int c; }s1; typedef struct s2 { char a; int c; char b; }s2; int main() { //內(nèi)存對齊的現(xiàn)象 printf("%d\n", sizeof(s1)); printf("%d\n", sizeof(s2)); return 0; }
很顯然這一段代碼就是計算s1與s2向系統(tǒng)申請的內(nèi)存大小
我:兩個char類型各為一,再加上應(yīng)該int類型的四,結(jié)果就是六
誠摯的喬治:你說的對,但不完全對,在結(jié)構(gòu)體中會出現(xiàn)內(nèi)存對齊的現(xiàn)象,不信?看結(jié)果
別慌,看到文章的最后,你(也許)就會恍然大悟
在結(jié)構(gòu)體中,內(nèi)存不是成員的大小之和
結(jié)構(gòu)體在內(nèi)存中開辟空間時內(nèi)存對齊的規(guī)則
1.結(jié)構(gòu)體中的第一個成員存放在這個結(jié)構(gòu)體的零偏移處,故第一個成員char類型的的偏移量為零
2.從第二個成員開始,每個成員都要對齊到成員對齊數(shù)的整數(shù)倍
(對齊數(shù)--成員自身大小與默認對齊數(shù)的最小值的整數(shù)倍,如果自身大小是四,默認對齊數(shù)是八,最終的對齊數(shù)就是四的倍數(shù)),一般情況下默認對齊數(shù)就是八。
3.結(jié)構(gòu)體的總大小必須是最大對齊數(shù)的整數(shù)倍 。
(最大對齊數(shù)就是每個成員對齊數(shù)中的最大值)
4.如果結(jié)構(gòu)體中嵌套結(jié)構(gòu)體的情況下,嵌套的結(jié)構(gòu)體就對齊到自己成員對齊數(shù)的最大對齊數(shù)的整數(shù)倍處,結(jié)構(gòu)體的總大小就是最大對齊數(shù)(含嵌套的結(jié)構(gòu)體成員)的整數(shù)倍。
下面其中的一個結(jié)構(gòu)體進行分析:
先向大家介紹一下本章的配角—offsetof
offsetof的返回值就是距離這個結(jié)構(gòu)體(自定義類型)起始位置的值
參數(shù)就是結(jié)構(gòu)體的名稱和結(jié)構(gòu)體成員的名稱。
#include<stddef.h> #include<stdio.h> typedef struct s { char a; int b; char c; }s; int main() { //offsetof-是指偏移量 printf("%d\n", offsetof(s,a)); printf("%d\n", offsetof(s,b)); printf("%d\n", offsetof(s,c)); return 0; }
char a;
//因為char a是結(jié)構(gòu)體第一個成員,所以偏移量就是零
int b;
//自身大小: 4 默認對齊數(shù):8 對齊數(shù):4的倍數(shù)即可,所以偏移量就是四
char c;
// 自身大小: 1 默認對齊數(shù):8 對齊數(shù):1的倍數(shù)即可,所以偏移量就是八
又因為最終的結(jié)構(gòu)體大小是成員最大對齊數(shù)的倍數(shù),也就是四的倍數(shù),所以最終的結(jié)構(gòu)體的大小應(yīng)該就是十二。
到這里是不是有一定的思路了,別急,再來一道試試吧
#include<stddef.h> #include<stdio.h> typedef struct s { double a; char b; int c; }s; int main() { //offsetof-是指偏移量 printf("%d\n", offsetof(s,a)); printf("%d\n", offsetof(s,b)); printf("%d\n", offsetof(s,c)); printf("%d", sizeof(s)); return 0; }
結(jié)果如下:
是不是跟你想的一樣呢?
同樣的,用一樣的方法進行解釋
double a;
//因為double a是結(jié)構(gòu)體第一個成員,所以偏移量就是零
char b;
//自身大?。? 默認對齊數(shù):8 對齊數(shù):1的倍數(shù)即可,所以偏移量就是八
int c;
//自身大?。? 默認對齊數(shù):8 對齊數(shù):4的倍數(shù)即可,所以偏移量就是十二
最終結(jié)構(gòu)體的大小就是四的整數(shù)倍十六
下面給應(yīng)該結(jié)構(gòu)體嵌套結(jié)構(gòu)體的例子
#include<stdio.h> #include<stddef.h> typedef struct s1 { char i; char j; int k; }s1; typedef struct s2 { char a; int c; s1; }s2; int main() { printf("%d\n", offsetof(s2, a)); printf("%d\n", offsetof(s2, c)); printf("%d", sizeof(s2)); return 0; }
我相信此時的你一定會計算結(jié)構(gòu)體想系統(tǒng)申請的大小,以及內(nèi)存對齊是咋回事
其實,內(nèi)存對齊也就那么回事兒~
為什么存在內(nèi)存對齊
知道怎樣計算后,你是否和我一樣思考
為什么內(nèi)存的申請不能想main函數(shù)中的內(nèi)存申請一樣,要多少就申請相應(yīng)大小的空間,這樣既省內(nèi)存,也不用考慮這么多。
1.平臺的原因
所謂平臺原因就是與硬件有關(guān),硬件不能訪問內(nèi)存中的每一個空間,換句話說,就是按一定的規(guī)律進行訪問,這樣內(nèi)存對齊就起到了很好的作用
2.性能的原因
數(shù)據(jù)結(jié)構(gòu)(尤其是棧),應(yīng)該盡可能的在自然邊界上對齊
因為我們的CPU訪問空間,就是一次性按四個字節(jié)的空間來訪問,內(nèi)存對齊在一定的時候避免了訪問一次的空間進行了二次訪問
如下訪問應(yīng)該int類型內(nèi)存對齊后只需要訪問一次
總的來說內(nèi)存對齊就是拿空間換取時間
當(dāng)然,我們可以實現(xiàn)內(nèi)存對齊之上,也可以進行省一定空間
就想博客開頭一道題,一模一樣的代碼,最終的結(jié)構(gòu)體的大小卻不一樣,這種就是優(yōu)化版的結(jié)構(gòu)體
這里給一個小的技巧,就是把空間小的成員盡量放在一起
到此這篇關(guān)于C語言結(jié)構(gòu)體中內(nèi)存對齊的問題理解的文章就介紹到這了,更多相關(guān)C語言 內(nèi)存對齊內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C語言開發(fā)實現(xiàn)通訊錄管理系統(tǒng)
這篇文章主要為大家詳細介紹了C語言開發(fā)實現(xiàn)通訊錄管理系統(tǒng),文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-08-08C++高并發(fā)內(nèi)存池的整體設(shè)計和實現(xiàn)思路
這篇文章主要介紹了C++高并發(fā)內(nèi)存池的整體設(shè)計和實現(xiàn)思路詳解,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-07-07C++的STL中accumulate函數(shù)的使用方法
這篇文章主要介紹了C++的STL中accumulate的使用方法,accumulate作用是累加求和即自定義類型數(shù)據(jù)處理,下文具體的操作方法需要的小伙伴可以參考一下2022-03-03C/C++使用socket實現(xiàn)判斷ip是否能連通
這篇文章主要為大家詳細介紹了C/C++如何使用socket實現(xiàn)判斷ip是否能連通,文中的示例代碼講解詳細,具有一定的學(xué)習(xí)價值,感興趣的小伙伴可以了解一下2023-07-07