記憶體對齊問題

2021-08-24 20:03:50 字數 2833 閱讀 3977

首先由乙個程式引入話題:

1//環境:vc6+windowssp22//

程式13

#include

<

iostream

>45

using

namespace

std;67

struct

st18;13

14struct

st215;20

21int

main()

2227

程式的輸出結果為:

sizeof(st1) is 12

sizeof(st2) is 8

問題出來了,這兩個一樣的結構體,為什麼sizeof的時候大小不一樣呢?

本文的主要目的就是解釋明白這一問題。

記憶體對齊,正是因為記憶體對齊的影響,導致結果不同。

對於大多數的程式設計師來說,記憶體對齊基本上是透明的,這是編譯器該幹的活,編譯器為程式中的每個資料單元安排在合適的位置上,從而導致了相同的變數,不同宣告順序的結構體大小的不同。

那麼編譯器為什麼要進行記憶體對齊呢?程式1中結構體按常理來理解sizeof(st1)和sizeof(st2)結果都應該是7,4(int) + 2(short) + 1(char) = 7 。經過記憶體對齊後,結構體的空間反而增大了。

在解釋記憶體對齊的作用前,先來看下記憶體對齊的規則:

每個特定平台上的編譯器都有自己的預設「對齊係數」(也叫對齊模數)。程式設計師可以通過預編

譯命令#pragma pack(n),n=1,2,4,8,16 來改變這一係數,其中的n 就是你要指定的「對齊係數」。vc6預設8位元組對齊

資料成員對齊規則:結構(struct)(或聯合(union))的資料成員,第乙個資料成員放在offset

為0 的地方,以後每個資料成員的對齊按照#pragma pack 指定的數值和這個資料成員自身長度中,比較小的那個進行。

min( #pragma pack()指定的數,這個資料成員的自身長度

)結構(或聯合)的整體對齊規則:在資料成員完成各自對齊之後,結構(或聯合)本身也要進

行對齊,對齊將按照#pragma pack 指定的數值和結構(或聯合)最大資料成員長度中,比較小的那個進行。

min(#pragma pack()指定的數,結構(或聯合)最大資料成員長度)。結構總大小必須為它的倍數

結合1、2 顆推斷:當#pragma pack 的n 值等於或超過所有資料成員長度的時候,這個n

值的大小將不產生任何效果。

乙個位域必須儲存在同乙個位元組中,不能跨兩個位元組。

以程式1為例解釋對齊的規則

st1 :char佔乙個位元組,起始偏移為0 ,int 佔4個位元組,min(#pragma pack()指定的數,這個資料成員的自身長度) = 4(vc6預設8位元組對齊),所以int按4位元組對齊,起始偏移必須為4的倍數,所以起始偏移為4,在char後編譯器會新增3個位元組的額外位元組,不存放任意資料。short佔2個位元組,按2位元組對齊,起始偏移為8,正好是2的倍數,無須新增額外位元組。到此規則1的資料成員對齊結束,此時的記憶體狀態為:

o***|oooo|oo

0123 4567 89 (位址)

(x表示額外新增的位元組)

共佔10個位元組。還要繼續進行結構本身的對齊,對齊將按照#pragma pack指定的數值和結構(或聯合)最大資料成員長度中,比較小的那個進行,st1結構中最大資料成員長度為int,佔4位元組,而預設的#pragma pack 指定的值為8,所以結果本身按照4位元組對齊,結構總大小必須為4的倍數,需新增2個額外位元組使結構的總大小為12 。此時的記憶體狀態為:

o***|oooo|ooxx

0123 4567 89ab (位址)

到此記憶體對齊結束。st1占用了12個位元組而非7個位元組。

st2 的對齊方法和st1相同,讀者可自己完成。

記憶體對齊的主要作用是:

平台原因(移植原因):不是所有的硬體平台都能訪問任意位址上的任意資料的;某些硬體平台只能在某些位址處取某些特定型別的資料,否則丟擲硬體異常。

效能原因:經過記憶體對齊後,cpu的記憶體訪問速度大大提公升。具體原因稍後解釋。

圖一:這是普通程式設計師心目中的記憶體印象,由乙個個的位元組組成,而cpu並不是這麼看待的。

圖二:cpu把記憶體當成是一塊一塊的,塊的大小可以是2,4,8,16位元組大小,因此cpu在讀取記憶體時是一塊一塊進行讀取的。塊大小成為memory access granularity

(粒度) 本人把它翻譯為「記憶體讀取粒度」 。

(32位作業系統記憶體位址應該是以4為增量,程式中int佔4位元組只能說他充分利用了這4位元組,而bool型剩下的高位3位元組應該是不使用的,待驗證)

假設cpu要讀取乙個int型4位元組大小的資料到暫存器中,分兩種情況討論:

資料從0位元組開始

資料從1位元組開始

再次假設記憶體讀取粒度為4。

圖三:當該資料是從0位元組開始時,很cpu只需讀取記憶體一次即可把這4位元組的資料完全讀取到暫存器中。

當該資料是從1位元組開始時,問題變的有些複雜,此時該int型資料不是位於記憶體讀取邊界上,這就是一類記憶體未對齊的資料。

圖四:此時cpu先訪問一次記憶體,讀取0—3位元組的資料進暫存器,並再次讀取4—5位元組的資料進暫存器,接著把0位元組和6,7,8位元組的資料剔除,最後合併1,2,3,4位元組的資料進暫存器。對乙個記憶體未對齊的資料進行了這麼多額外的操作,大大降低了cpu效能。

這還屬於樂觀情況了,上文提到記憶體對齊的作用之一為平台的移植原因,因為以上操作只有有部分cpu肯幹,其他一部分cpu遇到未對齊邊界就直接了。

記憶體對齊問題

一直困惑自己有兩個問題 1.程式為什麼要做記憶體對齊?1.處理器訪問記憶體是粒度為多位元組時,如果資料不是在邊界處,則,處理器需要分多個時鐘週期進行資料的訪問。2.增加可移植性。並非所有的處理器都可以訪問任何位址,可能出現硬體錯誤。具體可以參考 2.struct中如何計算記憶體對齊?先說說c語言中s...

記憶體對齊問題

首先由乙個程式引入話題 1 環境 vc6 windows sp22 程式13 include iostream 45 using namespace std 67 struct st1 8 1314 struct st215 20 21int main 2227 程式的輸出結果為 sizeof st...

記憶體對齊問題

一 什麼是位元組對齊,為什麼要對齊?現代計算機中記憶體空間都是按照byte劃分的,從理論上講似乎對任何型別的變數的訪問可以從任何位址開始,但實際情況是在訪問特定型別變數的時候經常在特定的記憶體位址訪問,這就需要各種型別資料按照一定的規則在空間上排列,而不是順序的乙個接乙個的排放,這就是對齊。對齊的作...