C語言結構體的記憶體儲存方式和位元組對齊

2021-10-04 01:31:46 字數 4038 閱讀 8381

環境:ubuntu64位 + gcc

先定義乙個空的結構體,通過sizeof檢視發現其占用記憶體位元組為0(在vc++6.0中為1),其他型別資料記憶體位元組大小如下圖

定義乙個非空結構體,再觀察其記憶體位元組大小,發現乙個結構體的大小並非各資料型別大小簡單地相加

為了體現這謎之樂趣,不妨換一種方式來測一測,觀察1和2,發現即使結構體成員都一樣,但是換了排序後,所佔的位元組數就發生改變;觀察3和4,float+char和float+int居然一樣大

上網查閱可知道,結構體的預設儲存方式採用以最大位元組元素位元組數對其方式進行對齊,所謂的位元組對齊,是指資料型別按照固定的位元組大小排列,方便計算機cpu、記憶體等的讀取。結構體中的資料型別並不是都相同的,這個時候就需要位元組對齊來提高計算機的讀取效率

這個固定的位元組大小,編譯器預設設為結構體內最大元素的位元組數,比如上述的float+char和float+int,其實儲存的時候,不管是int還是char,都是以4個位元組去儲存了,簡單來說就是,以後索引變數時的步進值均以那個固定位元組數訪問記憶體

拿下面的結構體來說明

struct student1

;

型別

位址1位址2

位址3位址4

int1位元組

1位元組1位元組

1位元組float

1位元組1位元組

1位元組1位元組

char+short

1位元組1位元組

1位元組填充

以最大元素對應的位元組數為固定位元組,上面的變數中float或int最大,所以是4(每一行存放的位元組個數);對於第三行的char+short,是因為在第三行一開始的時候,char乙個位元組存放完之後,還有三個位元組,足夠存放接下來的short,故char和short可以存放在同一行。3行4列,一共12個位元組。

對於相同元素的結構體2,其記憶體分布如下

struct student2

;

型別

位址1位址2

位址3位址4

short

1位元組1位元組

填充填充

int1位元組

1位元組1位元組

1位元組char

1位元組填充

填充填充

float

1位元組1位元組

1位元組1位元組

一開始在第一行先存short型,佔2個位元組,剩下的2個位元組空間並不夠接下來的int型4個位元組,因此只能另起一行,同理,當char型存在第三行後,剩下的3個位元組空間並不夠接下來的float型,則另起一行。4行4列,共16個位元組。因此可以知道為什麼結構體內部元素的排列順序會對結構體大小產生影響了

再看乙個例子來理解理解,student1的記憶體方式是3行8列,student2的則是2行8列,而student3僅在student2基礎上增加了乙個char,就又變成3行8列了,可知不同的位元組對齊方式對記憶體的影響還是很大的(此處注意,如果最後一行沒有滿8個,也必須要填充至8個,在本例結構體內存的大小必須要是8的整數倍)

(1) 結構體中元素對齊訪問主要原因是為了配合硬體,也就是說硬體本身有物理上的限制,如果對齊排布和訪問會提高效率,否則會大大降低效率。

(2) 記憶體本身是乙個物理器件(ddr記憶體晶元,soc上的ddr控制器),本身有一定的侷限性:如果記憶體每次訪問時按照4位元組對齊訪問,那麼效率是最高的;如果你不對齊訪問效率要低很多。

(3) 還有很多別的因素和原因,導致我們需要對齊訪問。譬如cache的一些快取特性,還有其他硬體(譬如mmu、lcd顯示器)的一些記憶體依賴特性,所以會要求記憶體對齊訪問。

(4) 對比對齊訪問和不對齊訪問:對齊訪問犧牲了記憶體空間,換取了速度效能;而非對齊訪問犧牲了訪問速度效能,換取了記憶體空間的完全利用。

小結:說白了,就是為訪問結構體成員效率高,也就是讀取資料更為高效(但是這裡也會犧牲一點點記憶體)。

值得開心的是,編譯器已經幫我們完成了對齊,正如前文所說,編譯器是預設以最大位元組數為基礎進行對齊的,其優點就是提高訪問效率,缺點是浪費一定的記憶體,至於浪費多還是少,是與寫**時對變數的排序有關的

一般的做法就是,位元組數少的變數盡量靠在一起,相同型別的變數也要靠在一起,這樣子就最大程度上減少記憶體浪費了

分享乙個面試題

怎樣使下面的結構體保持乙個位元組對齊或者說八字節對齊?

struct test;

在編譯器裡,我們可以通過對齊指令來設定結構體的對齊方式,有 #pragma pack() 和 #pragma pack(n) (n=1/2/4/8)

#pragma pack(n)告訴編譯器結構體或類內部的成員變數相對於第乙個變數的位址的偏移量的對齊方式,預設情況下,編譯器按照自然邊界對齊,當變數所需的自然對齊邊界比n大 時,按照n對齊,否則按照自然邊界對齊;#pragma pack () /取消指定對齊,恢復預設對齊/

通過上圖的測試(在win10+vs2019),發現使用乙個位元組對齊時,該結構體的記憶體為11,正好是 4+4+1+2,而沒有使用#pragma pack指令的相同結構體,則預設以最大位元組(即4位元組)進行排序;#prgma pack的方式在很多c環境下都是支援的,但是gcc雖然也可以,不過不建議使用(下圖是在gcc環境)

gcc支援但不推薦上述的對齊指令,如下圖演示,在gcc環境,對於兩個一樣的結構體,第乙個使用8位元組的對齊方式,第二個使用預設的4位元組,但是結果卻是一樣的,按照分析,第乙個如果設為8位元組對齊,該結構體大小就應該為16才對,因此可以知道編譯器還是把該結構體按照4位元組對齊處理了

從測試的結果來看是這樣:在gcc中,如果指定 #pragma pack(n) 中的 n 的話,n 不能大於預設對齊指定的長度,即如果預設對齊是 4 的話,n的取值可以是 1、2、4,超過 4 之後作為 4 處理。在 windows 等系統上似乎沒有這個限制

gcc推薦的對齊指令__attribute__((packed))和__attribute__((aligned(n)))

上述__attribute__ ((packed)) 的作用就是告訴編譯器,取消結構在編譯過程中的優化對齊,按照實際占用位元組數進行對齊,是gcc特有的語法。

上述__attribute__((aligned(m)))告訴編譯器乙個結構體或者類或者聯合或者乙個型別的變數(物件)分配位址空間時的位址對齊方式。也就是所,如果將__attribute__((aligned(m)))作用於乙個型別,那麼該型別的變數在分配位址空間時,其存放的位址一定按照m位元組對齊(m必須是2的冪次方)。並且其占用的空間,即大小,也是m的整數倍,以保證在申請連續儲存空間的時候,每乙個元素的位址也是按照m位元組對齊。

如下圖演示,三個結構體均是相同的,取消自動對齊即以1位元組方式對齊,此時結構體的大小為元素大小之和;使用__attribute__((aligned(m)))則可以設定其對齊方式,為2行8列

如有錯誤,還望指正!

結構體在記憶體中的儲存方式

乙個結構體變數定義完之後,其在記憶體中的儲存並不等於其所包含元素的寬度之和。例一 include using namespace std struct x s1 int main 在例一中的結構體變數s1定義之後,經測試,會發現sizeof s1 16,其值不等於sizeof s1.a 1 size...

C語言結構體的記憶體對齊

學過c語言的大家應該都學到過結構體,結構體是一種聚合資料型別,它可以把不同型別的資料儲存在一起,我們把結構體中儲存的資料叫做結構體成員。了解了結構體後,我們來談一談結構體在計算機系統中是如何儲存的,首先,我們來看看下面這段 struct a int main 這段 中我們分別定義了三個結構體成員,按...

C語言結構體的記憶體分配

原則一 結構體中元素按照定義順序存放到記憶體中,但並不是緊密排列。從結構體儲存的首位址開始 每乙個元素存入記憶體中時,它都會認為記憶體是以自己的寬度來劃分空間的,因此元素存放的位置一定會在自己大小的整數倍上開始。原則二 在原則一的基礎上,檢查計算出的儲存單元是否為所有元素中最寬的元素長度的整數倍。若...