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