__declspec 和 __attribute__

(1)__declspec

__declspec關鍵字詳細用法點擊打開鏈接

__declspec用於指定所給定類型的實例的與Microsoft相關的存儲方式。其它的有關存儲方式的修飾符如static與extern等是C和C++語言的ANSI規範,而__declspec是一種擴展屬性的定義。擴展屬性語法簡化並標準化了C和C++語言關於Microsoft的擴展。
用法:__declspec ( extended-decl-modifier )
extended-decl-modifier參數如下,可同時出現,中間有空格隔開:
align (C++)
allocate
appdomain
deprecated (C++)
dllimport
dllexport
jitintrinsic
naked (C++)
noalias
noinline
noreturn
nothrow (C++)
novtable
process
property(C++)
restrict
selectany
thread
uuid(C++)
1.__declspec關鍵字應該出現在簡單聲明的前面。對於出現在*或&後面或者變量聲明中標識符的前面的__declspec,編譯器將忽略並且不給出警告。
2.要注意區分__declspec是修飾類型還是修飾變量:
__declspec(align(8)) struct Str b;修飾的是變量b。其它地方定義的struct Str類型的變量將不受__declspec(align(8))影響。
__declspec(align(8)) struct Str {};修飾的是struct Str類型。所有該類型的變量都受__declspec(align(8))影響。

align
格式:__declspec(align(n)) declarator
其中,n是對齊參數,其有效值是2的整數次冪(從1到8192字節),如2,4,8,16,32或64。參數declarator是要設置對齊方式的數據。
1.使用__declspec(align(n))來精確控制用戶自定義數據的對齊方式。你可以在定義struct,union,class或聲明變量時使用__declspec(align(n))。
2.不能爲函數參數使用__declspec(align(n))。
3.如果未使用__declspec(align(#)),編譯器將根據數據大小按自然邊界對齊。如4字節整數按4字節邊界對齊;8字節double按8字節邊界對齊。類或結構體中的數據,將取數據本身的自然對齊方式和#pragma pack(n)設置的對齊係數中的最小值進行對齊。
4.__declspec(align(n))和#pragma pack(n)是一對兄弟,前者規定了對齊係數的最小值,後者規定了對齊係數的最大值。
5.當兩者同時出現時,前者擁有更高的優先級。即,當兩者同時出現且值矛盾時,後者將不起作用。
6.當變量size大於等於#pragma pack(n)指定的n,而且__declspec(align(n))指定的數值n比對應類型長度小的時候,這個__declspec(align(n))指定將不起作用。
7.當#pragma pack(n)指定的值n大於等於所有數據成員size的時候,這個值n將不起作用。

allocate
格式:__declspec(allocate("segname")) declarator
爲數據指定存儲的數據段。數據段名必須爲以下列舉中的一個:
code_seg
const_seg
data_seg
init_seg
section

appdomain
指定託管程序中的每個應用程序域都要有一份指定全局變量或靜態成員變量的拷貝。

deprecated
與#pragma deprecated()的作用相同。用於指定函數的某個重載形式是不推薦的。當在程序中調用了被deprecated修飾的函數時,編譯器將給出C4996警告,並且可以指定具體的警告信息。該警告信息可以來源於定義的宏。
例如:
// compile with: /W3
#define MY_TEXT "function is deprecated"
void func1(void) {}
__declspec(deprecated) void func1(int) {}
__declspec(deprecated("** this is a deprecated function **")) void func2(int) {}
__declspec(deprecated(MY_TEXT)) void func3(int) {}

int main() {
func1();
func1(1); // C4996,警告信息:warning C4996: 'func1': was declared deprecated
func2(1); // C4996,警告信息:warning C4996: 'func2': ** this is a deprecated function **
func3(1); // C4996,警告信息:warning C4996: 'func3': function is deprecated
}

dllimport,dllexport
格式:
__declspec( dllimport ) declarator
__declspec( dllexport ) declarator
分別用來從dll導入函數,數據,或對象以及從dll中導出函數,數據,或對象。相當於定義了dll的接口,爲它的客戶exe或dll定義可使用的函數,數據,或對象。
將函數聲明成dllexport就可以免去定義模塊定義(.DEF)文件。
dllexport代替了__export關鍵字。
被聲明爲dllexport的C++函數導出時的函數名將會按照C++規則經過處理。如果要求不按照C++規則進行名字處理,請使用.def文件或使用extern "C"。

jitintrinsic
格式:__declspec(jitintrinsic)
用於標記一個函數或元素是64位通用語言運行時(CLR)。主要用於Microsoft提供的某些庫中。
使用jitintrinsic會在函數簽名中加入MODOPT(IsJitIntrinsic)。

naked
格式:__declspec(naked) declarator
此關鍵字僅用於x86系統,多用於虛擬設備驅動。此關鍵字可以使編譯器在生成代碼時不包含任何註釋或標記。僅可以對函數的定義使用,不能用於數據聲明、定義,或者函數的聲明。

noalias
僅適用於函數,它指出該函數是半純粹的函數。半純粹的函數是指僅引用或修改局部變量、參數和第一層間接參數。它是對編譯器的一個承諾,如果該函數引用全局變量或第二層間接指針參數,則編譯器會生成中斷應用程序的代碼。

restrict
格式:__declspec(restrict) return_type f();
僅適用於返回指針的函數聲明或定義,如,CRT的malloc函數:__declspec(restrict) void *malloc(size_t size);它告訴編譯器該函數返回的指針不會與任何其它的指針混淆。它爲編譯器提供執行編譯器優化的更多信息。對於編譯器來說,最大的困難之一是確定哪些指針會與其它指針混淆,而使用這些信息對編譯器很有幫助。有必要指出,這是對編譯器的一個承諾,編譯器並不對其進行驗證。如果您的程序不恰當地使用__declspec(restrict),則該程序的行爲會不正確。

noinline
因爲在類定義中定義的成員函數默認都是inline的,__declspec(naked)用於顯式指定類中的某個函數不需要inline(內聯)。如果一個函數很小而且對系統性能影響不大,有必要將其聲明爲非內斂的。例如,用於處理錯誤情況的函數。

noreturn
一個函數被__declspec(noreturn)所修飾,那麼它的含義是告訴編譯器,這個函數不會返回,其結果是讓編譯器知道被修飾爲__declspec(noreturn)的函數之後的代碼不可到達。
如果編譯器發現一個函數有無返回值的代碼分支,編譯器將會報C4715警告,或者C2202錯誤信息。如果這個代碼分支是因爲函數不會返回從而無法到達的話,可以使用約定__declspec(noreturn)來避免上述警告或者錯誤。
將一個期望返回的函數約定爲__declspec(noreturn)將導致未定義的行爲。
在下面的這個例子中,main函數沒有從else分支返回,所以約定函數fatal爲__declspec(noreturn)來避免編譯或警告信息。
__declspec(noreturn) extern void fatal () {}
int main() {
if(1)
return 1;
else if(0)
return 0;
else
fatal();
}

nothrow:
格式:return-type __declspec(nothrow) [call-convention] function-name ([argument-list])
可用於函數聲明。告訴編譯器被聲明的函數以及函數內部調用的其它函數都不會拋出異常。

novtable
可用於任何類聲明中,但最好只用於純接口類,即類本身從不實例化。此關鍵字的聲明將阻止編譯器對構造和析構函數的vfptr的初始化。可優化編譯後代碼大小。
如果試圖實例化一個用__declspec(novtable)聲明的類然後訪問類中成員,則會在運行時產生訪問錯誤(access violation,即AV)。

process
表示你的託管應用程序進程應該擁有一份指定全局變量,靜態成員變量,或所有應用程序域共享的靜態本地變量的拷貝。在使用/clr:pure進行編譯時,應該使用__declspec(process),因爲使用/clr:pure進行編譯時,在默認情況下,每個應用程序域擁有一份全局和靜態變量的拷貝。在使用/clr進行編譯時,不必使用__declspec(process),因爲使用/clr進行編譯時,在默認情況下,每個進程有一份全局和靜態變量的拷貝。
只有全局變量,靜態成員變量,或本地類型的本地靜態變量可以用__declspec(process)修飾。
在使用/clr:pure進行編譯時,被聲明爲__declspec(process)的變量同時也應該聲明爲const類型。
如果想每個應用程序域擁有一份全局變量的拷貝時,請使用appdomain。

property
格式:
__declspec( property( get=get_func_name ) ) declarator
__declspec( property( put=put_func_name ) ) declarator
__declspec( property( get=get_func_name, put=put_func_name ) ) declarator
該屬性可用於類或結構定義中的非靜態“虛數據成員”。實際上就是做了一個映射,把你的方法映射成屬性,以供訪問。get和put就是屬性訪問的權限,一個是讀的權限,一個是寫的權限。當編譯器看到被property修飾的數據成員出現在成員選擇符("." 或 "->")的右邊的時候,它將把該操作轉換成get或put方法。該修飾符也可用於類或結構定義中的空數組。
用法如下:
struct S {
int i;
void putprop(int j) {
i = j;
}
int getprop() {
return i;
}
__declspec(property(get = getprop, put = putprop)) int the_prop;
};

int main() {
S s;
s.the_prop = 5;
return s.the_prop;
}

selectany
格式:__declspec(selectany) declarator
在MFC,ATL的源代碼中充斥着__declspec(selectany)的聲明。selectany可以讓我們在.h文件中初始化一個全局變量而不是隻能放在.cpp中。比如有一個類,其中有一個靜態變量,那麼我們可以在.h中通過類似__declspec(selectany) type class::variable = value;這樣的代碼來初始化這個全局變量。既是該.h被多次include,鏈接器也會爲我們剔除多重定義的錯誤。對於template的編程會有很多便利。
用法如下:

__declspec(selectany) int x1=1; //正確,x1被初始化,並且對外部可見

const __declspec(selectany) int x2 =2; //錯誤,在C++中,默認情況下const爲static;但在C中是正確的,其默認情況下const不爲static

extern const __declspec(selectany) int x3=3; //正確,x3是extern const,對外部可見

extern const int x4;
const __declspec(selectany) int x4=4; //正確,x4是extern const,對外部可見

extern __declspec(selectany) int x5; //錯誤,x5未初始化,不能用__declspec(selectany)修飾

class X {
public:
X(int i){i++;};
int i;
};

__declspec(selectany) X x(1); //正確,全局對象的動態初始化

thread
格式:__declspec(thread) declarator
聲明declarator爲線程局部變量並具有線程存儲時限,以便鏈接器安排在創建線程時自動分配的存儲。
線程局部存儲(TLS)是一種機制,在多線程運行環境中,每個線程分配自己的局部數據。在標準多線程程序中,數據是在多個線程間共享的,而TLS是一種爲每個線程分配自己局部數據的機制。
該屬性只能用於數據或不含成員函數的類的聲明和定義,不能用於函數的聲明和定義。
該屬性的使用可能會影響DLL的延遲載入。
該屬性只能用於靜態數據,包括全局數據對象(static和extern),局部靜態對象,類的靜態數據成員;不能用於自動數據對象。
該屬性必須同時用於數據的聲明和定義,不管它的聲明和定義是在一個文件還是多個文件。
__declspec(thread)不能用作類型修飾符。
如果在類聲明的同時沒有定義對象,則__declspec(thread)將被忽略,例如:

// compile with: /LD
__declspec(thread) class X
{
public:
int I;
} x; //x是線程對象
X y; //y不是線程對象

下面兩個例子從語義上來說是相同的:

__declspec(thread) class B {
public:
int data;
} BObject; //BObject是線程對象

class B2 {
public:
int data;
};
__declspec(thread) B2 BObject2; // BObject2是線程對象

uuid
格式:__declspec( uuid("ComObjectGUID") ) declarator
將具有唯一標識符號的已註冊內容聲明爲一個變量,可使用__uuidof()調用。
用法如下:
struct __declspec(uuid("00000000-0000-0000-c000-000000000046")) IUnknown;
struct __declspec(uuid("{00020400-0000-0000-c000-000000000046}")) IDispatch;

(2)__attribute__點擊打開鏈接

GNU C的一大特色(卻不被初學者所知)就是__attribute__機制。__attribute__可以設置函數屬性(Function Attribute)、變量屬性(Variable Attribute)和類型屬性(Type Attribute)。
__attribute__書寫特徵是:__attribute__前後都有兩個下劃線,並切後面會緊跟一對原括弧,括弧裏面是相應的__attribute__參數。
__attribute__語法格式爲:
__attribute__ ((attribute-list))
其位置約束爲:
放於聲明的尾部“;”之前。
函數屬性(Function Attribute)
函數屬性可以幫助開發者把一些特性添加到函數聲明中,從而可以使編譯器在錯誤檢查方面的功能更強大。__attribute__機制也很容易同非GNU應用程序做到兼容之功效。
GNU CC需要使用 –Wall編譯器來擊活該功能,這是控制警告信息的一個很好的方式。下面介紹幾個常見的屬性參數。
__attribute__ format
該__attribute__屬性可以給被聲明的函數加上類似printf或者scanf的特徵,它可以使編譯器檢查函數聲明和函數實際調用參數之間的格式化字符串是否匹配。該功能十分有用,尤其是處理一些很難發現的bug。
format的語法格式爲:
format (archetype, string-index, first-to-check)
format屬性告訴編譯器,按照printf, scanf, strftime或strfmon的參數表格式規則對該函數的參數進行檢查。“archetype”指定是哪種風格;“string-index”指定傳入函數的第幾個參數是格式化字符串;“first-to-check”指定從函數的第幾個參數開始按上述規則進行檢查。
具體使用格式如下:
__attribute__((format(printf,m,n)))
__attribute__((format(scanf,m,n)))
其中參數m與n的含義爲:
m:第幾個參數爲格式化字符串(format string);
n:參數集合中的第一個,即參數“…”裏的第一個參數在函數參數總數排在第幾,注意,有時函數參數裏還有“隱身”的呢,後面會提到;
在使用上,__attribute__((format(printf,m,n)))是常用的,而另一種卻很少見到。下面舉例說明,其中myprint爲自己定義的一個帶有可變參數的函數,其功能類似於printf:
//m=1;n=2
extern void myprint(const char *format,...) __attribute__((format(printf,1,2)));
//m=2;n=3
extern void myprint(int l,const char *format,...) __attribute__((format(printf,2,3)));
[NextPage]
需要特別注意的是,如果myprint是一個函數的成員函數,那麼m和n的值可有點“懸乎”了,例如:
//m=3;n=4
extern void myprint(int l,const char *format,...) __attribute__((format(printf,3,4)));
其原因是,類成員函數的第一個參數實際上一個“隱身”的“this”指針。(有點C 基礎的都知道點this指針,不知道你在這裏還知道嗎?)
這裏給出測試用例:attribute.c,代碼如下:
1:
2:extern void myprint(const char *format,...) __attribute__((format(printf,1,2)));
3:
4:void test()
5:{
6: myprint("i=%d\n",6);
7: myprint("i=%s\n",6);
8: myprint("i=%s\n","abc");
9: myprint("%s,%d,%d\n",1,2);
10:}

運行$gcc –Wall –c attribute.c attribute後,輸出結果爲:

attribute.c: In function `test':
attribute.c:7: warning: format argument is not a pointer (arg 2)
attribute.c:9: warning: format argument is not a pointer (arg 2)
attribute.c:9: warning: too few arguments for format

如果在attribute.c中的函數聲明去掉__attribute__((format(printf,1,2))),再重新編譯,既運行$gcc –Wall –c attribute.c attribute後,則並不會輸出任何警告信息。
注意,默認情況下,編譯器是能識別類似printf的“標準”庫函數。
__attribute__ noreturn
該屬性通知編譯器函數從不返回值,當遇到類似函數需要返回值而卻不可能運行到返回值處就已經退出來的情況,該屬性可以避免出現錯誤信息。C庫函數中的abort()和exit()的聲明格式就採用了這種格式,如下所示:

extern void exit(int) __attribute__((noreturn));
extern void abort(void) __attribute__((noreturn));

爲了方便理解,大家可以參考如下的例子:

//name: noreturn.c ;測試__attribute__((noreturn))
extern void myexit();

int test(int n)
{
if ( n > 0 )
{
myexit();
/* 程序不可能到達這裏*/
}
else
return 0;
}

編譯顯示的輸出信息爲:

$gcc –Wall –c noreturn.c
noreturn.c: In function `test':
noreturn.c:12: warning: control reaches end of non-void function

警告信息也很好理解,因爲你定義了一個有返回值的函數test卻有可能沒有返回值,程序當然不知道怎麼辦了!
[NextPage]

加上__attribute__((noreturn))則可以很好的處理類似這種問題。把
extern void myexit();
修改爲:
extern void myexit() __attribute__((noreturn));
之後,編譯不會再出現警告信息。
__attribute__ const
該屬性只能用於帶有數值類型參數的函數上。當重複調用帶有數值參數的函數時,由於返回值是相同的,所以此時編譯器可以進行優化處理,除第一次需要運算外,其它只需要返回第一次的結果就可以了,進而可以提高效率。該屬性主要適用於沒有靜態狀態(static state)和副作用的一些函數,並且返回值僅僅依賴輸入的參數。
爲了說明問題,下面舉個非常“糟糕”的例子,該例子將重複調用一個帶有相同參數值的函數,具體如下:

extern int square(int n) __attribute__((const));
...
for (i = 0; i < 100; i )
{
total = square(5) i;
}

通過添加__attribute__((const))聲明,編譯器只調用了函數一次,以後只是直接得到了相同的一個返回值。
事實上,const參數不能用在帶有指針類型參數的函數中,因爲該屬性不但影響函數的參數值,同樣也影響到了參數指向的數據,它可能會對代碼本身產生嚴重甚至是不可恢復的嚴重後果。
並且,帶有該屬性的函數不能有任何副作用或者是靜態的狀態,所以,類似getchar()或time()的函數是不適合使用該屬性的。
-finstrument-functions
該參數可以使程序在編譯時,在函數的入口和出口處生成instrumentation調用。恰好在函數入口之後並恰好在函數出口之前,將使用當前函數的地址和調用地址來調用下面的 profiling 函數。(在一些平臺上,__builtin_return_address不能在超過當前函數範圍之外正常工作,所以調用地址信息可能對profiling函數是無效的。)

void __cyg_profile_func_enter(void *this_fn, void *call_site);
void __cyg_profile_func_exit(void *this_fn, void *call_site);

其中,第一個參數this_fn是當前函數的起始地址,可在符號表中找到;第二個參數call_site是指調用處地址。
instrumentation 也可用於在其它函數中展開的內聯函數。從概念上來說,profiling調用將指出在哪裏進入和退出內聯函數。這就意味着這種函數必須具有可尋址形式。如果函數包含內聯,而所有使用到該函數的程序都要把該內聯展開,這會額外地增加代碼長度。如果要在C 代碼中使用extern inline聲明,必須提供這種函數的可尋址形式。
可對函數指定no_instrument_function屬性,在這種情況下不會進行instrumentation操作。例如,可以在以下情況下使用no_instrument_function屬性:上面列出的profiling函數、高優先級的中斷例程以及任何不能保證profiling正常調用的函數。
no_instrument_function
如果使用了-finstrument-functions ,將在絕大多數用戶編譯的函數的入口和出口點調用profiling函數。使用該屬性,將不進行instrument操作。
constructor/destructor
若函數被設定爲constructor屬性,則該函數會在main()函數執行之前被自動的執行。類似的,若函數被設定爲destructor屬性,則該函數會在main()函數執行之後或者exit()被調用後被自動的執行。擁有此類屬性的函數經常隱式的用在程序的初始化數據方面。
這兩個屬性還沒有在面向對象C中實現。
[NextPage]
同時使用多個屬性
可以在同一個函數聲明裏使用多個__attribute__,並且實際應用中這種情況是十分常見的。使用方式上,你可以選擇兩個單獨的__attribute__,或者把它們寫在一起,可以參考下面的例子:

/* 把類似printf的消息傳遞給stderr 並退出 */
extern void die(const char *format, ...)
__attribute__((noreturn))
__attribute__((format(printf, 1, 2)));

或者寫成

extern void die(const char *format, ...)
__attribute__((noreturn, format(printf, 1, 2)));

如果帶有該屬性的自定義函數追加到庫的頭文件裏,那麼所以調用該函數的程序都要做相應的檢查。

和非GNU編譯器的兼容性
慶幸的是,__attribute__設計的非常巧妙,很容易作到和其它編譯器保持兼容,也就是說,如果工作在其它的非GNU編譯器上,可以很容易的忽略該屬性。即使__attribute__使用了多個參數,也可以很容易的使用一對圓括弧進行處理,例如:

/* 如果使用的是非GNU C, 那麼就忽略__attribute__ */
#ifndef __GNUC__
# define __attribute__(x) /*NOTHING*/
#endif

需要說明的是,__attribute__適用於函數的聲明而不是函數的定義。所以,當需要使用該屬性的函數時,必須在同一個文件裏進行聲明,例如:

/* 函數聲明 */
void die(const char *format, ...) __attribute__((noreturn))
__attribute__((format(printf,1,2)));

void die(const char *format, ...)
{
/* 函數定義 */
}

更多的屬性含義參考:
http://gcc.gnu.org/onlinedocs/gcc-4.0.0/gcc/Function-Attributes.html

變量屬性(Variable Attributes)
關鍵字__attribute__也可以對變量(variable)或結構體成員(structure field)進行屬性設置。這裏給出幾個常用的參數的解釋,更多的參數可參考本文給出的連接。
在使用__attribute__參數時,你也可以在參數的前後都加上“__”(兩個下劃線),例如,使用__aligned__而不是aligned,這樣,你就可以在相應的頭文件裏使用它而不用關心頭文件裏是否有重名的宏定義。
[NextPage]

aligned (alignment)
該屬性規定變量或結構體成員的最小的對齊格式,以字節爲單位。例如:

int x __attribute__ ((aligned (16))) = 0;

編譯器將以16字節(注意是字節byte不是位bit)對齊的方式分配一個變量。也可以對結構體成員變量設置該屬性,例如,創建一個雙字對齊的int對,可以這麼寫:

struct foo { int x[2] __attribute__ ((aligned (8))); };

如上所述,你可以手動指定對齊的格式,同樣,你也可以使用默認的對齊方式。如果aligned後面不緊跟一個指定的數字值,那麼編譯器將依據你的目標機器情況使用最大最有益的對齊方式。例如:

short array[3] __attribute__ ((aligned));

選擇針對目標機器最大的對齊方式,可以提高拷貝操作的效率。
aligned屬性使被設置的對象佔用更多的空間,相反的,使用packed可以減小對象佔用的空間。
需要注意的是,attribute屬性的效力與你的連接器也有關,如果你的連接器最大隻支持16字節對齊,那麼你此時定義32字節對齊也是無濟於事的。
packed
使用該屬性可以使得變量或者結構體成員使用最小的對齊方式,即對變量是一字節對齊,對域(field)是位對齊。
下面的例子中,x成員變量使用了該屬性,則其值將緊放置在a的後面:

struct test
{
char a;
int x[2] __attribute__ ((packed));
};

其它可選的屬性值還可以是:cleanup,common,nocommon,deprecated,mode,section,shared,tls_model,transparent_union,unused,vector_size,weak,dllimport,dlexport等,
詳細信息可參考:
http://gcc.gnu.org/onlinedocs/gcc-4.0.0/gcc/Variable-Attributes.html#Variable-Attributes
類型屬性(Type Attribute)
關鍵字__attribute__也可以對結構體(struct)或共用體(union)進行屬性設置。大致有六個參數值可以被設定,即:aligned, packed, transparent_union, unused, deprecated 和 may_alias。
在使用__attribute__參數時,你也可以在參數的前後都加上“__”(兩個下劃線),例如,使用__aligned__而不是aligned,這樣,你就可以在相應的頭文件裏使用它而不用關心頭文件裏是否有重名的宏定義。
aligned (alignment)
該屬性設定一個指定大小的對齊格式(以字節爲單位),例如:

struct S { short f[3]; } __attribute__ ((aligned (8)));
typedef int more_aligned_int __attribute__ ((aligned (8)));
[NextPage]
該聲明將強制編譯器確保(盡它所能)變量類型爲struct S或者more-aligned-int的變量在分配空間時採用8字節對齊方式。
如上所述,你可以手動指定對齊的格式,同樣,你也可以使用默認的對齊方式。如果aligned後面不緊跟一個指定的數字值,那麼編譯器將依據你的目標機器情況使用最大最有益的對齊方式。例如:

struct S { short f[3]; } __attribute__ ((aligned));

這裏,如果sizeof(short)的大小爲2(byte),那麼,S的大小就爲6。取一個2的次方值,使得該值大於等於6,則該值爲8,所以編譯器將設置S類型的對齊方式爲8字節。
aligned屬性使被設置的對象佔用更多的空間,相反的,使用packed可以減小對象佔用的空間。
需要注意的是,attribute屬性的效力與你的連接器也有關,如果你的連接器最大隻支持16字節對齊,那麼你此時定義32字節對齊也是無濟於事的。
packed
使用該屬性對struct或者union類型進行定義,設定其類型的每一個變量的內存約束。當用在enum類型定義時,暗示了應該使用最小完整的類型(it indicates that the smallest integral type should be used)。
下面的例子中,my-packed-struct類型的變量數組中的值將會緊緊的靠在一起,但內部的成員變量s不會被“pack”,如果希望內部的成員變量也被packed的話,my-unpacked-struct也需要使用packed進行相應的約束。

struct my_unpacked_struct
{
char c;
int i;
};

struct my_packed_struct
{
char c;
int i;
struct my_unpacked_struct s;
}__attribute__ ((__packed__));

其它屬性的含義見:
http://gcc.gnu.org/onlinedocs/gcc-4.0.0/gcc/Type-Attributes.html#Type-Attributes
變量屬性與類型屬性舉例
下面的例子中使用__attribute__屬性定義了一些結構體及其變量,並給出了輸出結果和對結果的分析。
程序代碼爲:

struct p
{
int a;
char b;
char c;
}__attribute__((aligned(4))) pp;

struct q
{
int a;
char b;
struct n qn;
char c;
}__attribute__((aligned(8))) qq;


[NextPage]

int main()
{
printf("sizeof(int)=%d,sizeof(short)=%d.sizeof(char)=%d\n",sizeof(int),sizeof(short),sizeof(char));
printf("pp=%d,qq=%d \n", sizeof(pp),sizeof(qq));

return 0;
}

輸出結果:

sizeof(int)=4,sizeof(short)=2.sizeof(char)=1
pp=8,qq=24

分析:

sizeof(pp):
sizeof(a) sizeof(b) sizeof(c)=4 1 1=6<23=8= sizeof(pp)
sizeof(qq):
sizeof(a) sizeof(b)=4 1=5
sizeof(qn)=8;即qn是採用8字節對齊的,所以要在a,b後面添3個空餘字節,然後才能存儲qn,
4 1 (3) 8 1=17
因爲qq採用的對齊是8字節對齊,所以qq的大小必定是8的整數倍,即qq的大小是一個比17大又是8的倍數的一個最小值,由此得到
17<24 8=24= sizeof(qq)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章