1. 程式人生 > >VC記憶體對齊準則

VC記憶體對齊準則

本文所有內容在建立在一個前提下:使用VC編譯器。著重點在於:VC的記憶體對齊準則;同樣的資料, 不同的排列有不同的大小,另外在有虛擬函式或虛擬繼承情況下又有如何影響?

記憶體對齊?!What?Why?

對於一臺32位的機器來說如何才能發揮它的最佳存取效率呢?當然是每次都讀4位元組(32bit), 這樣才可以讓它的bus處於最高效率。實際上它也是這麼做的,即使你只需要一個位元組,它也是 讀一個機器字長(這兒是32bit)。更重要的是,有的機器在存取或儲存資料的時候它要求資料 必須是對齊的,何謂對齊?它要求資料的地址從4的倍數開始,如若不然,它就報錯。還有的機 器它雖然不報錯,但對於一個類似int變數,假如它橫跨一個邊界的兩端,那麼它將要進行兩次 讀取才能獲得這個int值。比方它儲存在地址為2~5的四個位元組中,那麼要讀取這個int,將要 進行兩次讀取,第一次讀取0~3四個位元組,第二次讀取4~7四個位元組。但是如果我們把這個整 形的起始地址調整到0,4,8…呢?一次存取就夠了!這種調整就是記憶體對齊了。我們也可以依次 類推到16位或64位的機器上。

邊界該如何調整

對於32位的機器來說,它當然最渴望它的資料的大小都是4 Byte或者4的倍數Byte,這樣它就能 最有效率的存取資料,當然如果資料小於4Byte,那也是沒問題的。那麼編譯器要做的便是儘量滿 足這個要求。

這兩天我斷續對VC做了一些實驗,並總結如下三條準則,你要明白的是這並非來自微軟的官方文 檔,但我自以為這些準則或許不全但應該都是正確的:

  • 變數存放的起始位置2應為變數的大小與規定對齊量1中較小者的倍數。例如,假 設規定對齊量為4,那麼char(1byte)變數應該儲存在偏移量為1的倍數的地方,而整形變 量(4byte)則是從偏移量為4的倍數的地方,而double(8 byte)也同樣應儲存在偏移量為 4的倍數的地方,為什麼不是8?因為規定對齊量預設值為4,而4 < 8。在VC中預設對齊量 為8,而非4
  • 結構體整體的大小也應該對齊,對齊依照規定對齊量與最大資料成員兩者中較小的進行。
  • Vptr影響對齊而VbcPoint(Virtual base class pointer)不影響。

一個例項

對於類T:

class T{
    char c;
    int i;
    double  d;};

將其sizeof輸出後的大小為16,其記憶體佈局如圖T.變數c從偏移量為0開始儲存,而整形i第一個 符號條件的偏移量為4,double型d的第一個符號條件的為8。整個物件的大小為16,不需要再進 行額外的對齊。

圖T(類T 的記憶體佈局) :

同樣的資料,不同的大小

再看類L,它與T儲存同樣型別的資料,僅僅是順序不同罷了,那麼它sizeof輸出的大小是多少呢?

類L:

class L{
    char c;
    double  d;
    int i;};

它sizeof後的結果或許會令你大吃一驚,或許不會(如果你有認真讀前面的兩條準則)。L sizeof後的結果是24!同樣是一個int,一個char,一個double卻整整多出了8個位元組。這期間 發生了什麼?我們依據前面兩條規則來看看。C儲存於0的位置,1~7都不能整除8,所以d儲存 在8~15,16給i正好合適,i儲存在16~19。總共花費了20個位元組,抱歉不是8的倍數,還得補 齊4個。現在你可以看看圖L的關於類L的記憶體佈局,再比較一下類L和類T的記憶體佈局。

圖L(類L的佈局)

我得出了這樣一條並不權威的結論,因為我還沒聽有人這樣說過:在宣告資料成員的時候,將 最大位元組數的變數放在最前面3,切忌不要將大小差距很大的型別交替宣告。

Vptr影響對齊而VbcPoint(Virtual base class pointer)不影響

前面的例項只涉及前兩條準則,現在我們來看看第三條的兩個例項:

class X{char a;};
class Y: virtual public X{};

Y的大小為:a佔一個位元組,VbcPoint(我稱他為虛基類指標)佔四個位元組。我們不論a與VbcPoint 的位置如何擺放,如果將VbcPoint等同於一個成員資料來看的話,sizeof(Y)都應該為8.實際上 它是5!就我目前的水平,我只能先將其解釋為VbcPoint不參與對齊。

對於vptr這個問題則不存在:

class X{
    char a;
    virtual int vfc(){};}

sizeof(X)的大小確實為8.

關於#pragma pack(n)

用#pragma pack(n)改變規定對齊量試試眨眼

  1. 規定對齊量:實際上並沒有這麼一個名詞,是我為了方便而造出來的。在VC中這個“規定對齊量”會有一個預設值,這個預設值一般為8,我原來一直以為這個值以為是4,至於它為什麼為8,我現在還不知道。。我們也可以通過#pargma pack(n)來規定這個值,目前n可以為1,2,4,8,16。 

  2. 這個起始位置指的是相對於結構體(類)來說的。 

  3. 此處有一點問題,這個問題由獨酌逸醉提出,他認為將最小的資料放在最前面可能會更好,我們有進行過討論,但可惜的是由於在2011/11/24日資料庫丟失,我只能用備份還原,所以丟失了一些資料,無疑,本文的評論也在其中。不過我對這個問題映像深刻,因為我在寫這篇部落格的時候便困惑於到底成員是應該放在之前還是之後,因為這兩種情況我都找不到強有力的理由來支撐它們。後來使我確信從大到小排列好於從小到大排列的理由在於,從大到小排列一般無需成員之間的對齊,唯一的對齊工作是最後進行的整個結構體對齊的工作。毫無疑問的是,這應該是最節省記憶體的方式。再之後,獨酌提出從小到大可能好些,雖然沒有給出有說服力的理由,但卻使我無比困惑,我當時雖然認為從大到小的排列更有優勢,但卻實在想不出一個例項能使得它優於從小到大排列的。不過最終我擊垮了自己的理由,在繼承狀況下從大到小排列很容易被打破,比方,基類的成員為一個char,繼承類的成員為double,int,char雖然基類和繼承類都是按從大到小的順序排列的,但是繼承類的記憶體佈局最終會使char,double,int,char,此時既不能避免成員對齊,又導致後面的結構體對齊。暫時獲得的最終結果是從小到大排列是更好的一種排列方式。(2011/12/31增補) 

    -----------------

    class X{};
    class Y: virtual public X{char a;};
    sizeof(Y)得到的卻是8
    在VS2008上試的。

相關推薦

VC記憶體準則

本文所有內容在建立在一個前提下:使用VC編譯器。著重點在於:VC的記憶體對齊準則;同樣的資料, 不同的排列有不同的大小,另外在有虛擬函式或虛擬繼承情況下又有如何影響? 記憶體對齊?!What?Why? 對於一臺32位的機器來說如何才能發揮它的最佳存取效率呢?當然是每次

VC++平臺上的記憶體操作

我們知道當記憶體的邊界正好對齊在相應機器字長邊界上時,CPU的執行效率最高,為了保證效率,在VC++平臺上記憶體對齊都是預設開啟的,在32位機器上記憶體對齊的邊界為4位元組;比如看如下的程式碼: struct MyStruct { int i;

vc記憶體

#include<stdio.h> typedefstruct MyStruct    {    double dda1;    char dda;    int type;   }MyStruct;    typedefstruct MyStruct2 

struct自然邊界上的記憶體

    記憶體對齊大多數情況對程式設計師是透明的,是由編譯器自動處理。在C裡面允許我們干預記憶體對齊。而由於記憶體對齊的原因,巧妙的設計結構體也是非常必要的。   關於記憶體對齊問題,字、雙字和四字在自然邊界上不需要在記憶體中對齊,對字、雙字和四字來

C++虛擬函式表以及記憶體文章

C++虛擬函式表以及記憶體對齊文章 C++ 物件的記憶體佈局(上) https://blog.csdn.net/haoel/article/details/3081328 C++ 物件的記憶體佈局(下) https://blog.csdn.net/haoel/article/deta

結構體記憶體模式

結構體的位元組大小,一個簡單的結構體定義如下,這個結構的大小應是8位元組(32位下) typedef struct MODEL4 { char c; int x; }MODEL4; char的大小是1,而int是4,但總的大小是8,這就是結構體記憶體對齊的原因。在32位的機器上,資料是以

C++11 記憶體 alignof alignas

一 現象 先看一段程式碼: struct s1 { char s; int i; }; struct s2 { int i; double d; }; cout << "-------basic type" << endl; c

行內函數,巨集定義,記憶體,型別轉換

巨集 與 inline的區別 存在的價值,兩者都是文字替換,降低程式跳轉次數,提高效率 1. define 是預處理命令,無法除錯 ,最簡單文字替換,     inline 是編譯期替換,可以除錯, 存在引數型別檢查 2. 使用inline的時候,函式必須定義   直接定義的函式

記憶體演算法

--------------------------------------------- -- 時間:2018-11-09 -- 建立人:Ruo_Xiao -- 郵箱:[email protected] ----------------------------------------

關於記憶體

記憶體對齊可以用一句話來概括:“資料項只能儲存在地址是資料項大小的整數倍的記憶體位置上” 例如int型別佔用4個位元組,地址只能在0,4,8等位置上。 位元組對齊的緣故,如下的結構體的佔用記憶體是一樣 struct A{       &

現代計算機記憶體機制

64位計算機和32位計算機CPU對記憶體處理的區別 64位CPU,位寬為8個位元組。(64位/8位/位元組=8位元組) 32位CPU,位寬位4個位元組。(32位/8位/位元組=4位元組) 我們假想記憶體空間是一個二階矩陣。(事實上記憶體是一維線性排列的) 那麼二維陣列的列數在64位C

結構體記憶體總結

  首先我們都知道結構體是多個變數的集合,在其中可以存放整型,浮點型等等各種,然後結構體記憶體是如何對齊的呢,他並不是按連續順序去排下去的,首先我們先上一段程式碼 #include<iostream> #include<cstdlib> using namesp

結構體的大小 記憶體

結構體的大小 記憶體對齊   Struct A {    int a;    int b; };  int main() { Printf(“%d\n”,sizeof(struct A)); 共佔8位

記憶體和補

  對齊:是針對單個成員變數的; 補齊:是針對擺放的所有成員變數的整體而言要對齊; //4位元組的對齊粒度 #pragma  pack(4)       // #pragma pack(n) /* n = 1,

初夏小談:結構體記憶體詳解

記憶體對齊?什麼是記憶體對齊? 對於這個問題我們先來看看這樣的一個結構體(在32位系統下) typedef struct Stu1 { char C1; int num1; short S1; }Stu1; 如果我們不知道記憶體對齊或者不清楚記憶體對齊時,我們可能這樣分析

關於記憶體的問題

在最近的專案中,我們涉及到了“記憶體對齊”技術。對於大部分程式設計師來說,“記憶體對齊”對他們來說都應該是“透明的”。“記憶體對齊”應該是編譯器的 “管轄範圍”。編譯器為程式中的每個“資料單元”安排在適當的位置上。但是C語言的一個特點就是太靈活,太強大,它允許你干預“記憶體對齊”。如果你想了解 更加

為何要記憶體

 為何要記憶體對齊 http://www.ibm.com/developerworks/library/pa-dalign/ 因為處理器讀寫資料,並不是以位元組為單位,而是以塊(2,4,8,16位元組)為單位進行的。如果不進行對齊,那麼本來只需要一次進行的訪問,可能需要好幾次才能完成,並且還要進

C語言結構體(記憶體問題)

C語言結構體對齊也是老生常談的話題了。基本上是面試題的必考題。內容雖然很基礎,但一不小心就會弄錯。寫出一個struct,然後sizeof,你會不會經常對結果感到奇怪?sizeof的結果往往都比你宣告的變數總長度要大,這是怎麼回事呢?     開始學的時候,

記憶體的要素--資料成員的規則

Data structure alignment refers to the way data is arranged and accessed in computer memory. It consists of three separate but related issues:&nb

結構體深度剖析(記憶體引數,偏移量)

一、瞭解結構體 1 在C語言中,除了最常見用資料型別,字元型別(char)、整數型別(short、int、long )、實型(float、double),,,,,,最常見也是最經典的還有一種資料型別,那就是結構體。  二、結構體經典面試題:  (1)、什麼是結構體?