【轉載】【內存對齊(二)】__declspec( align(#) )的用法和大小計算

 

轉自:http://www.cppblog.com/deercoder/archive/2011/03/13/141747.htmlhtml

感謝做者!java

 

上面講到了關於pack的內存對齊和計算方法,這裏繼續講實現內存對齊的另外一種方式:__declspec( align(#) )c++

__declspec( align(#) )和#pragma pack( n )有密切聯繫。

當一個變量或結構體同時受二者影響時,前者的優先級高。程序員

成員的地址決定於前者及後者,其要麼是前者的倍數,要麼是後者的倍數,要麼是成員的大小的倍數,取最小。算法

結構體最後的大小於前者有關,其要麼是前者的倍數,要麼是結構體中最大偏移量的倍數,取最大。數組

要算出最後結果,必須知道二者的值或缺省值。函數

 

下面舉一個例子來詳細的分析:測試

 

#include <stdio.h>

#include "stdafx.h"
#include <stdlib.h>
//using namespace std;

#pragma pack( push, 4 )

__declspec( align(32) )struct D
{
    int i1;
    double d1;
    int i2;
    int i3;
};

int main()
{
    cout << "sizeof(int) = "<<sizeof(int) << endl;
    cout << "sizeof(char) = " << sizeof(char) << endl;
    cout << "sizeof(double) = " << sizeof(double) << endl;
    cout << sizeof(D) << endl;
    system("PAUSE");
    return 0;
}

 

這段代碼在VS 2010中的運行結果是,sizeof(D)的大小爲32,而在Dev C++,C-Free 5.0以及gcc中的結果都彷佛20(轉載時編輯:QT下mingw編譯器也爲20)。下面咱們來着重講講關於__declspec( align(#) )的用法:spa

正如前面所說的,當有__declspec( align(#) )和pack的時候,__declspec( align(#) )的優先級要高些。因此對於上面這個例子,咱們首先來計算出來每個的大小。.net

 

  1. 成員的地址如何取?

規則:成員的地址要取pack(n),__declspec( align(m) ),以及成員自身大小這三者之間的最小值,也就是,min(n,m,sizeof(成員變量類型)),那麼咱們能夠對每個結構體的成員都進行分析。

 

第一個爲int類型,佔據4B,因此地址是[0~3].

第二個爲double類型,它的地址要根據min(4,32,sizeof(double))來判斷,因此應該是4的倍數,也就是相鄰着int類型的i1存放。地址是[4~11]。

第三個爲int類型,佔據4B,一樣應該是4的倍數,地址是[12~15].

第四個爲int類型,佔據4B,地址爲[16~19].

 

從而總的地址是從[0~19]連續存放的20個字節,那麼是否sizeof(D)的大小就是20呢?

 

通過測試,咱們能夠看到,在VS 2010中,結果是32,why?

 

這就要用__declspec( align(#) )來解釋了。也就是下面第二點的內容。

 

  1. 結構體最後的大小如何決定?

規則:結構體最後的大小與__declspec( align(m) )有關,其要麼是它的倍數,要麼是結構體中最大偏移量的倍數,取最大。

 

根據這個規則,這裏align是32,而結構體中最大的是double類型,也就是應該是max(32,8)=32,因此最後結構體的大小應該是32的倍數,而明顯上面咱們看到的實際大小是20B,從而須要擴展到32B。

 

在這裏,就體現了__declspec( align(m) )的強大做用!

 

一樣的,爲了體現該語句的做用,咱們去掉這個語句,運用咱們前面一節內容的知識,來計算並測試sizeof(D),最終不管是在VS 2010仍是Dev C++中,運行的結果都是上面咱們所預測的20B。

 

OK,下面回到最後的疑問,也就是前面咱們提出的,爲什麼加入了__declspec( align(m) )語句以後,在DevC++和VS 2010的結果不一樣?

 

實際上,對於這些內存對齊的處理,不一樣的編譯器可能採起不一樣的處理,就像前面一節中所說的,我將pack誤用爲package,致使根本沒有達到按照我要求的字節對齊的目的,並且編譯器根本不提供任何警告信息。那麼,這裏合理的解釋是:Dev C++不支持這種用法。

 

經過查閱資料,參照這篇文章【 SSE指令介紹及其C、C++應用 】(http://blog.csdn.net/delphihero/archive/2006/09/24/1270069.aspx),咱們能夠看到做者有這麼一段話:

 

「接下來我舉一個例子來講明SSE的指令函數是如何使用的,必需要說明的是我如下的代碼都是在VC7.1的平臺上寫的,不保證對其它如Dev-C++、Borland C++等開發平臺的徹底兼容。」

 

「這裏要注意一下,我使用了__declspec(align(16))作爲數組定義的修釋符,這表示該數組是以16字節爲邊界對齊的,由於SSE指令只能支持這種格式的內存數據。

  咱們在這裏看到了SSE算的強大,相信它會成爲多媒體程序員手中用來對付無窮盡流媒體數據的一把利劍。我後面還會寫一些關於SSE算法更復雜應用的文章,敬請關注,感謝您抽時間閱讀!

 

從這篇文章咱們能夠看到,SSE指令集的狀況下,在VC 7.1下才支持__declspec(align(16))這種用法,而對於其餘平臺不必定有效。而前面咱們使用的Dev C++以及C-Free,都是基於g++或者MinGW,不必定會支持這種方式,或者說,不必定按照這種內存對齊的建議來作,也就形成告終果的不一樣。

 

 

下面咱們來繼續探討結構體中有結構體的狀況。

 

先看看下面這段代碼:

 

#include <stdio.h>

#include "stdafx.h"
#include <stdlib.h>
//using namespace std;

#pragma pack( push, 4 )

__declspec( align(32) )struct D
{
    int i1;
    double d1;
    int i2;
    int i3;
};

__declspec( align(16) ) struct E
{
     int i1;
     D m_d;
     int i2;
};

int main()
{
    cout << "sizeof(int) = "<<sizeof(int) << endl;
    cout << "sizeof(char) = " << sizeof(char) << endl;
    cout << "sizeof(double) = " << sizeof(double) << endl;
    cout << sizeof(D) << endl;
    cout << sizeof(E) << endl;
    system("PAUSE");
    return 0;
}

 

最後運行的結果是sizeof(E)爲96,爲什麼會是這個結果呢?咱們來詳細講解下。

 

對於結構體E,第一個元素爲int類型,因此佔據[0~3]地址單元。

第二個元素是一個結構體,該結構體因爲受上面__declspec( align(32) )的影響,優先級高,因此起始地址是32的倍數,並且大小爲32B,從而應該放置在[32~63]單元處。

最後一個是int類型的變量,大小爲4,因此應該是4的倍數,地址爲[64~67]。

 

故結構體E的大小應該是從[0~67],佔據68B,而因爲前面還有限制__declspec( align(16) ),同時成員變量的最大偏移是sizeof(D)=32,因此咱們最後這個結構體的大小應該是他們中最大值的倍數,也就是32的倍數,68向上取32的倍數應該是96.故結果爲96.

 

最後仍然是上面平臺的問題,在Dev C++和G++下面的結果不一樣,緣由上面解釋了。


MSDN:

「The sizeof value for any structure is the offset of the final member, plus that member's size, rounded up to the nearest multiple of the largest member alignment value or the whole structure alignment value, whichever is greater.」

中文:

「sizeof的結果都是結構體中最後的一個成員變量加上它的大小,再加上一個填充容量(padding),這個填充大小是成員變量最大的一個對齊參數或整個結構體的對齊參數的倍數,取哪一個決定於哪一個對齊參數較大」

 

ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.en/dv_vclang/html/e4209cbb-5437-4b53-b3fe-ac264501d404.htm

ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.en/dv_vclang/html/9cb63f58-658b-4425-ac47-af8eabfc5878.htm




P.S.:上面是關於內存對齊的研究,若有謬誤,歡迎指出!


附參考資料和拓展:

1. #pragma pack :http://blog.sina.com.cn/s/blog_492aa57901008y3h.html 
2. #pragma pack( n )和__declspec( align(#) ) 的偏移量計算方法: http://blog.csdn.net/whoismickey/archive/2009/03/28/4032155.aspx
3. #pragma pack(push,1) (pop) :http://blog.csdn.net/jiang1013nan/archive/2009/11/25/4861248.aspx
4. 關於pragma pack的用法(四) C++中的內存對齊問題: http://www.cppblog.com/xczhang/archive/2007/12/23/39396.html
5. SSE指令介紹及其C、C++應用:http://blog.csdn.net/delphihero/archive/2006/09/24/1270069.aspx
6. c++中__declspec用法總結: http://sealbird.javaeye.com/blog/855096

相關文章
相關標籤/搜索