詳解C語言如何計算結構體大小(結構體的內存對齊)
引言:
當我們對計算結構體一無所知,我們不妨自己思索如何計算,是不是直接計算結構體成員變量占用內存的大小呢?
那我們先舉個例子
struct s1 { int i; char a; char b; }; struct s2 { char a; int i; char b; }; int main() { printf("%d\n", sizeof(struct s1)); printf("%d\n", sizeof(struct s2)); return 0; }
觀察發(fā)現(xiàn)結構體的大小計算跟我們想的很不一樣。
不應該是兩個char類型,一個int類型,2*1+4答案不應該是6嗎?
上面兩個結構體內容是一樣的,只有順序不一樣,為何計算結果不一樣呢?
我們就帶著以上的疑問去探索!
一、計算偏移量
我們要研究明白結構體的成員列表在內存中到底是如何存儲的,首先要知道結構體的各個成員變量在內存中相較于起始位置的偏移量。這時候要引用到offsetof,這個宏可以計算結構體成員相較于結構體起始位置的偏移量。
使用宏offsetof
如何使用宏offsetof?
首先有頭文件:#include<stddef.h>
參數(shù)是類型,和成員名,返回值就是結構體成員相較于結構體起始位置的偏移量。
我們先試著打印下s2各個成員關于結構體起始位置的偏移量。
發(fā)現(xiàn)結果是0、4、8,我們可以畫一張內存圖進行理解。
如圖所示,根據(jù)offsetof我們可以得到這樣的內存存儲模式,但是這樣一共也就9個字節(jié),后面的3個字節(jié)從何而來?中間多出來的3個字節(jié)又從何而來?
我們繼續(xù)探索。
結構體到底如何計算?
二、結構體的對齊規(guī)則
我們經過上面的分析,發(fā)現(xiàn)結構體成員不是按照順序在內存中連續(xù)存放的,而是有一定的對齊規(guī)則,接下來我們就研究結構體的內存規(guī)則。
- 結構體的第一個成員永遠放在相較于結構體變量的起始未知的偏移量為0的位置
- 從第二個成員開始,往后的每個成員都要對齊到某個對齊數(shù)的整數(shù)倍處。(對齊數(shù):結構體成員自身大小和默認對齊數(shù)的較小值)VS上默認對齊數(shù)是8,gcc沒有默認對齊數(shù),對齊數(shù)就是變量本身的大小。
- 結構體的總大小,必須是最大對齊數(shù)的整數(shù)倍,最大對齊數(shù)是:所有成員的對齊數(shù)中最大的值
- 如果嵌套了結構體的情況,嵌套的結構體對齊到自己的最大對齊數(shù)的整數(shù)倍處,結構體的整體大小就是所有最大對齊數(shù)(含嵌套結構體的對齊數(shù))的整數(shù)倍。
三、總結計算方法
我們首先要知道結構體變量成員的自身字節(jié)大小,然后去尋找對齊數(shù),對齊數(shù)的尋找方法就是將自身字節(jié)大小和默認對齊數(shù)比較,取較小值,這樣先找到對齊數(shù),然后根據(jù)自身的字節(jié)大小去填充,就完成了成員在內存中的存儲,最后在所有的成員已經結束存儲,再計算最大對齊數(shù)(所有成員的對齊數(shù)中最大值),這樣就完成了計算!
我們既然已經知道規(guī)則和計算方法,就讓我們小試牛刀一下~
四、練習
練習一:
struct s3 { double d; char c; int i; }; int main() { printf("%d\n", sizeof(struct s3)); return 0; }
上面圖片的寫法就是左邊是本身成員變量的字節(jié)大小,右邊是默認對齊數(shù)進行比較,最后再從對齊數(shù)中找出最大值,就是最大對齊數(shù),所以最后0~15就是存儲結構體的大小,也就是一共16個字節(jié)
練習二:
struct S3 { double d; char c; int i; }; struct S4 { char c1; struct S3 s3; double d; }; int main() { printf("%d\n", sizeof(struct S4)); return 0; }
上面是嵌套結構體場景,結構體S3本身大小是16,需要對齊到自身最大對齊數(shù)的位置,也就是8,然后double類型的對齊數(shù)是8,最后總字節(jié)大小也滿足最大對齊數(shù),所以一共32個字節(jié)。
五、為什么存在內存對齊?
1、平臺原因
不是所有的硬件平臺都能訪問任意地址上的任意數(shù)據(jù);某些平臺只能在某些地址處取某些地址處取特定類型的數(shù)據(jù),否則拋出硬件異常
2、性能原因
數(shù)據(jù)結構(尤其是棧)應該盡可能在自然邊界上對齊。原因在于,為了訪問未對齊的內存,處理器需要作兩次內存訪問;而對齊的內存訪問僅需要一次訪問。
總體來說
結構體的內存對齊,就是讓空間換時間。
TIP:
我們在設計結構體時,可以人為的節(jié)省空間——讓占用空間小的成員盡量集中在一起。
例如我們之前舉的例子,盡管兩個結構體存的成員變量一樣,但是順序不一樣,結構體內存大小也是不同。
六、修改默認對齊數(shù)
對,你沒有聽錯,默認對齊數(shù)是可以修改滴,當我們把默認對齊數(shù)修改為1時,結構體的成員變量就是連續(xù)存儲的。代碼如下,計算出來的大小就是4+1+8=13
#pragma pack(1)//修改默認對齊數(shù)為1 struct s { int a; char b; double c; }; #pragma pack()//修改默認對齊數(shù)為默認 int main() { printf("%d\n", sizeof(struct s)); return 0; }
到此這篇關于詳解C語言如何計算結構體大小(結構體的內存對齊)的文章就介紹到這了,更多相關C語言計算結構體大小內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
解析bitmap處理海量數(shù)據(jù)及其實現(xiàn)方法分析
本篇文章是對bitmap處理海量數(shù)據(jù)及其實現(xiàn)的方法進行了詳細的分析介紹,需要的朋友參考下2013-05-05C++ Boost Chrono實現(xiàn)計時碼表流程詳解
Boost是為C++語言標準庫提供擴展的一些C++程序庫的總稱。Boost庫是一個可移植、提供源代碼的C++庫,作為標準庫的后備,是C++標準化進程的開發(fā)引擎之一,是為C++語言標準庫提供擴展的一些C++程序庫的總稱2022-11-11C語言數(shù)據(jù)結構實例講解單鏈表的實現(xiàn)
單鏈表是后面要學的雙鏈表以及循環(huán)鏈表的基礎,要想繼續(xù)深入了解數(shù)據(jù)結構以及C++,我們就要奠定好這塊基石!接下來就和我一起學習吧2022-03-03