1. 程式人生 > >C99結構體指定初始化

C99結構體指定初始化

在閱讀GNU/Linux核心程式碼時,我 們會遇到一種特殊的結構初始化方式。該方式是某些C教材(如譚二版、K&R二版)中沒有介紹過的。這種方式稱為指定初始化(designated initializer)。下面我們看一個例子,Linux-2.6.x/drivers/usb/storage/usb.c中有這樣一個結構體初始化 專案: static struct usb_driver usb_storage_driver = {        .owner = THIS_MODULE,        .name = "usb-storage",        .probe = storage_probe,        .disconnect = storage_disconnect,        .id_table = storage_usb_ids, };     乍一看,這與我們之前學過的結構體初始化差距甚遠。其實這就是前面所說的指定初始化在Linux裝置驅動程式中的一個應用,它源自ISO C99標準。以下我摘錄了C Primer Plus第五版中相關章節的內容,從而就可以很好的理解2.6版核心採用這種方式的優勢就在於由此初始化不必嚴格按照定義時的順序。這帶來了極大的靈活 性,其更大的益處還有待大家在開發中結合自身的應用慢慢體會。     已知一個結構,定義如下 struct book {     char title[MAXTITL];     char author[MAXAUTL];     float value; };     C99支援結構的指定初始化專案,其語法與陣列的指定初始化專案近似。只是,結構的指定初始化專案使用點運算子和成員名(而不是方括號和索引值)來標識具體的元素。例如,只初始化book結構的成員value,可以這樣做:     struct book surprise = { .value = 10.99 };     可以按照任意的順序使用指定初始化專案:     struct book gift = { .value = 25.99,                                     .author = "James Broadfool",                                     .title = "Rue for the Toad"};     正像陣列一樣,跟在一個指定初始化專案之後的常規初始化專案為跟在指定成員後的成員提供了初始值。另外,對特定成員的最後一次賦值是它實際獲得的值。例如,考慮下列宣告:     struct book gift = { .value = 18.90,                                     .author = "Philionna pestle",                                     0.25};     這將把值0.25賦給成員value,因為它在結構宣告中緊跟在author成員之後。新的值0.25代替了早先的賦值18.90。     有關designated initializer的進一步資訊可以參考c99標準的6.7.8節Ininialization。

標準C89需要初始化語句的元素以固定的順序出現,和被初始化的陣列或結構體中的元素順序一樣。

在ISO C99中,你可以按任何順序給出這些元素,指明它們對應的陣列的下標或結構體的成員名,並且GNU C也把這作為C89模式下的一個擴充套件。這個擴充套件沒有在GNU C++中實現。

為了指定一個數組下標,在元素值的前面寫上“[index] =”。比如:

int a[6] = { [4] = 29, [2] = 15 };

相當於:

int a[6] = { 0, 0, 15, 0, 29, 0 };

下標值必須是常量表達式,即使被初始化的陣列是自動的。

一個可替代這的語法是在元素值前面寫上“.[index]”,沒有“=”,但從GCC 2.5開始就不再被使用,但GCC仍然接受。 為了把一系列的元素初始化為相同的值,寫為“[first ... last] = value”。這是一個GNU擴充套件。比如:

int widths[] = { [0 ... 9] = 1, [10 ... 99] = 2, [100] = 3 };

如果其中的值有副作用,這個副作用將只發生一次,而不是範圍內的每次初始化一次。

注意,陣列的長度是指定的最大值加一。

在結構體的初始化語句中,在元素值的前面用“.fieldname = ”指定要初始化的成員名。例如,給定下面的結構體,

struct point { int x, y; };

和下面的初始化,

struct point p = { .y = yvalue, .x = xvalue };

等價於:

struct point p = { xvalue, yvalue };

另一有相同含義的語法是“.fieldname:”,不過從GCC 2.5開始廢除了,就像這裡所示:

struct point p = { y: yvalue, x: xvalue };

[index]”或“.fieldname”就是指示符。在初始化共同體時,你也可以使用一個指示符(或不再使用的冒號語法),來指定共同體的哪個元素應該使用。比如:

union foo { int i; double d; };

union foo f = { .d = 4 };

將會使用第二個元素把4轉換成一個double型別來在共同體存放。相反,把4轉換成union foo型別將會把它作為整數i存入共同體,既然它是一個整數。(參考5.24節向共同體型別轉換。)

你可以把這種命名元素的技術和連續元素的普通C初始化結合起來。每個沒有指示符的初始化元素應用於陣列或結構體中的下一個連續的元素。比如,

int a[6] = { [1] = v1, v2, [4] = v4 };

等價於

int a[6] = { 0, v1, v2, 0, v4, 0 };

當下標是字元或者屬於enum型別時,標識陣列初始化語句的元素特別有用。例如:

int whitespace[256]
  = { [' '] = 1, ['/t'] = 1, ['/h'] = 1,
      ['/f'] = 1, ['/n'] = 1, ['/r'] = 1 };

你也可以在“=”前面寫上一系列的“.fieldname”和“[index]”指示符來指定一個要初始化的巢狀的子物件;這個列表是相對於和最近的花括號對一致的子物件。比如,用上面的struct point宣告:

struct point ptarray[10] = { [2].y = yv2, [2].x = xv2, [0].x = xv0 };

如果同一個成員被初始化多次,它將從最後一次初始化中取值。如果任何這樣的覆蓋初始化有副作用,副作用發生與否是非指定的。目前,gcc會捨棄它們併產生一個警告。

你可以在單個case標籤中指定一系列連續的值,就像這樣:

case low ... high:

這和單獨的case標籤的合適數字有同樣的效果,每個對應包含在從low到high中的一個整數值。

這個特性對一系列的ASCII字元程式碼特別有用:

case 'A' ... 'Z':

當心:在...周圍寫上空格,否則當你把它和整數值一起使用時,它就會被解析出錯。例如,這樣寫:

case 1 ... 5:

而不是:

case 1...5:

向共同體型別轉換和其它轉換類似,除了指定的型別是一個共同體型別。你可以用union tag或一個typedef名字來指定型別。向共同體轉換實際上卻是一個構造,而不是一個轉換,因此不像普通轉換那樣產生一個左值。(參考5.21節複合文字)

可以向共同體型別轉換的型別是共同體中成員的型別。所以,給定下面的共同體和變數:

union foo { int i; double d; };
int x;
double y;

x和y都能夠被轉換成型別union foo

把這種轉換作為給共同體變數賦值的右側和在這個共同體的成員中儲存是等價的:

union foo u;

u = (union foo) x  ==  u.i = x
u = (union foo) y  ==  u.d = y

你也可以使用共同體轉換作為函式引數。

void hack (union foo);

hack ((union foo) x);

ISO C99和ISO C++允許宣告和程式碼在複合語句中自由地混合。作為一個擴充套件,GCC在C89模式下也允許這樣。比如,你可以:

int i;

i++;
int j = i + 2;

每個識別符號從它被宣告的地方到閉合控制塊結束都是可見的。

在GNU C中,你可以宣告關於在你程式中呼叫的函式的某些東西,來幫助編譯器優化函式呼叫和更仔細地檢查你的程式碼。

關鍵字__attribute__允許你在宣告時指定特殊的屬性。跟在這個關鍵字後面的是雙重圓括號裡面的屬性說明。有十四個屬性noreturn, pure, const, format, format_arg, no_instrument_function, section, constructor, destructor, unused, weak, malloc, alias and no_check_memory_usage是目前為函式定義的。在特別的目標系統上,也給函式定義了一些其它屬性。其它屬性,包括section都為變數宣告(參考5.33節 指定變數屬性)和型別(參考5.34節 指定型別屬性)所支援。

你也可以把“__”放在每個關鍵字的前面和後面來指定屬性。這允許你在標頭檔案中使用它們,而不用關心一個可能有相同名字的巨集。比如,你可以使用__noreturn__而不是noreturn

noreturn

一些標準庫函式,就像abortexit,不能返回。GCC會自動了解到這一點。一些程式定義它們自己的從不返回的函式。你可以把它們宣告為noreturn來告訴編譯器這個事實。比如,

關鍵字noreturn告訴編譯器去假設fatal不能返回。那它就能做優化,而不用理會如果fatal返回會發生什麼。這會產生稍微好一點兒的程式碼。更重要的是,它有助於避免未初始化變數的偽造警告。

不要假設呼叫函式儲存的暫存器在呼叫noreturn函式之前被恢復。

對於一個noreturn函式,有一個除void之外的返回型別是毫無意義的。

在早於2.5版的GCC中沒有實現noreturn屬性。宣告不返回值的函式的一個可替代的方法,在當前版本和一些舊的版本中都可以工作,如下:

pure

很多函式除了返回值外沒有作用,而且它們的返回值只取決於引數和/或全域性變數。這樣的一個函式可能依附於普通的子表示式的消除和迴圈的優化,就像一個算術操作符那樣。這些函式應該用屬性pure來宣告。例如,

說明假定的函式square可以安全地比程式中說的少呼叫幾次。

pure函式的一些常見例子是strlenmemcmp。有趣的非pure函式是帶無限迴圈,或者那些取決於易失性記憶體或其它系統資源的函式,它們可能在兩次連續的呼叫中間改變(比如在多執行緒環境中的feof)。

pure屬性在GCC早於2.96的版本中沒有實現。

const

很多函式不檢查除它們的引數外的任何值,而且除返回值外沒有任何作用。基本上,這比上面的pure屬性稍微更嚴格一些,既然函式不允許去讀全域性記憶體。

注意,帶指標引數,而且檢查所指向資料的函式不能宣告為const。同樣的,呼叫非const函式的函式通常也不能是const。一個const函式返回void是沒任何意義的。

屬性const在GCC早於2.5的版本中沒有實現。宣告一個函式沒有副作用的一個可替代的方式,能夠在當前版本和一些舊的版本中工作,如下:

這種方法在2.6.0以後的GNU C++不起作用,既然語言指明const必須依附於返回值。

format (archetype, string-index, first-to-check)

format屬性指明一個函式使用printf,scanf,strftimestrfmon風格的引數,應該通過格式化字串進行型別檢查。比如,宣告:

會促使編譯器檢查呼叫my_printf中的引數和printf風格的格式化字串引數my_format是否一致。

引數archetype決定格式化字串是怎麼被解釋的,而且應當是printf,scanf,strftimestrfmon。(你也可以使用__printf__,__scanf__,__strftime__或者__strfmon__。)引數string-index指定哪個引數是格式化字串引數(從1開始),而first-to-check是通過格式化字串檢查的第一個引數。對於引數不可用來檢查的函式(比如vprintf),指定第三個引數為0。在這種情況下,編譯器只檢查格式化字串的一致性。對於strftime格式,第三個引數需要為0。

在上面的例子中,格式化字串(my_format)是my_printf函式的第二個引數,而且要檢查的函式從第三個引數開始,所以format屬性的正確引數是2和3。

format屬性允許你去識別你自己的把格式化字串作為引數的函式,所以GCC可以檢查對這些函式的呼叫錯誤。編譯器總是(除非使用了“-ffreestanding”)為標準庫函式printf,fprintf,sprintf,scanf,fscanf,sscanf,strftime,vprintf,vfprintfvsprintf檢查格式,當請求這種警告時(使用“-Wformat”),所以沒有必要修改標頭檔案stdio.h。在C99模式下,函式snprintf,vsnprintf,vscanf,vfscanfvsscanf也被檢查。參考“控制C方言的選項”一節。

format_arg (string-index)

format_arg屬性指明一個函式使用printf,scanf,strftimestrfmon風格的引數,而且修改它(比如,把它翻譯成其它語言),所以結果能夠傳遞給一個printf,scanf,strftimestrfmon風格的函式(格式化函式的其餘引數和它們在不修改字串的函式中一樣)。例如,宣告:

促使編譯器檢查呼叫printf,scanf,strftimestrfmon型別的函式中的引數,其格式化字串引數是函式my_dgettext函式的呼叫,和格式化字串引數my_format是否一致。如果format_arg屬性沒有被指定,在對格式化函式的這種中,編譯器所能告知的一切是格式化字串引數不是常量;當使用“-Wformat-nonliteral”時,這會產生一個警告,但如果沒有屬性,呼叫將不會被檢查。

引數string-index指定哪個引數是格式化字串(從1開始)。

format-arg屬性允許你去識別你自己的修改格式化字串的函式,那樣,GCC可以檢查對printf,scanf,strftimestrfmon型別函式的呼叫,它們的運算元是對你自己的一個函式的呼叫。編譯器總是以這種方式對待gettext,dgettextdcgettext,除了當嚴格的ISO C支援通過“-ansi”或者一個合適的“-std”選項請求時,或者“-ffreestanding”使用時。參考“控制C方言的選項”一節。

no_instrument_function

如果給定“-finstrument-functions”,在大多數使用者編譯的函式的入口和出口會生成對概要分析函式的呼叫。有這個屬性的函式將不會被測量。

section ("section-name")

通常,編譯器會把它生成的程式碼放入text部分。有時,然而,你需要額外的部分,或者你需要某些特別的函數出現在特別的部分。section屬性指定一個函式放入一個特別的部分。比如,宣告:

把函式foobar放進bar部分。

一些檔案格式不支援任意部分,所以section屬性並不是在所有平臺上可用的。如果你需要把一個模組的全部內容對映到一個特別的部分,考慮使用連結器的工具。

constructor

destructor

constructor屬性促使函式在執行main()之前自動被呼叫。類似地,destructor屬性促使函式在main()函式完成或exit()被呼叫完之後自動被呼叫。有這些屬性的函式對初始化在程式執行期間間接使用的資料很有用。

這些屬性目前沒有為Objective C所實現。

unused

這個屬性,依附於一個函式,意味著這個函式將可能打算不被使用。GCC將不會為這個函式產生一個警告。GNU C++目前不支援這個屬性,因為沒有引數的定義在C++中是合法的。

weak

weak屬性促使宣告被作為一個弱符號匯出,而不是全域性符號。這在定義庫函式時非常有用,它們能夠被使用者程式碼覆蓋,雖然它也可以和非函式宣告一起使用。弱符號被ELF目標檔案所支援,而且當使用GNU彙編器和連結器時也被a.out目標檔案支援。

malloc

malloc屬性用來告訴編譯器一個函式可以被當做malloc函式那樣。編譯器假設對malloc的呼叫產生一個不能替換成其它東西的指標。

alias ("target")

alias屬性促使這個宣告被作為另一個必須被指定的符號的別名匯出。例如,

宣告“f”是“__f”的一個弱別名。在C++中,目標的重整名字必須被使用。

並不是所有的目標機器支援這個屬性。

no_check_memory_usage

no_check_memory_usage屬性促使GCC忽略記憶體引用的檢查,當它為函式生成程式碼時。通常如果你指定“-fcheck-memory-usage”(參考“3.18 程式碼生成轉換選項”一節),GCC在大多數記憶體訪問之前生成呼叫支援的例程,以便允許支援程式碼記錄用法和探測未初始化或未分配儲存空間的使用。既然GCC不能恰當處理asm語句,它們不允許出現在這樣的函式中。如果你用這個屬性聲明瞭一個函式 ,GCC將會為那個函式生成記憶體檢查程式碼,允許使用asm語句,而不必用不同選項去編譯那個函式。這允許你編寫自己的支援例程如果你願意,而不會導致無限遞迴,如果它們用“-fcheck-memory-usage”編譯的話。

regparm (number)

在Intel 386上,regparm屬性促使編譯器用暫存器EAX,EDX,和ECX,而不是堆疊,來傳遞最多number個引數。帶可變引數數目的函式將會繼續在堆疊上傳遞它們的引數。

stdcall

在Intel 386上,stdcall屬性促使編譯器假定被呼叫的函式將會彈出用來傳遞引數的堆疊空間,除非它適用可變數目的引數。

cdecl

在Intel 386上,cdecl屬性促使編譯器假定呼叫函式將會彈出用來傳遞引數的堆疊空間。這對覆蓋“-mrtd”開關的作用很有幫助。

PowerPC上Windows NT的編譯器目前忽略cdecl屬性。

longcall

在RS/6000和PowerPC上,longcall屬性促使編譯器總是通過指標來呼叫函式,所以在距當前位置超過64MB(67108864位元組)的函式也能夠被呼叫。

long_call/short_call

這個屬性允許指定如果在ARM上呼叫一個特別的函式。兩個屬性都覆蓋“-mlong-calls”命令列開關和#pragma long_calls設定。long_call屬性促使編譯器總是通過先裝入它的地址到一個暫存器再使用那個暫存器的內容來呼叫這個函式。short_call屬性總是直接把從呼叫現場到函式的偏移量放進‘BL’指令中。

dllimport

在執行Windows NT的PowerPC上,dllimport屬性促使編譯器通過一個全域性指標去呼叫函式,這個指標指向由Windows NT的dll庫安裝的函式指標。指標名是通過組合__imp__和函式名來形成的。

dllexport

在執行Windows NT的PowerPC上,dllexport屬性促使編譯器提供一個指向函式指標的全域性指標,那樣它就能用dllimport屬性呼叫。指標名是通過組合__imp__和函式名來形成的。

exception (except-func [, except-arg])

在執行Windows NT的PowerPC上,exception屬性促使編譯器修改為宣告函式匯出的結構化異常表的表項。字串或識別符號except-func被放在結構化異常表的第三項中。它代表一個函式,當異常發生時被異常處理機制呼叫。如果它被指定,字串或識別符號except-arg被放在結構化異常表的第四項中。

function_vector

在H8/300和H8/300H上使用這個選項用表明指定的函式應該通過函式向量來呼叫。通過函式向量呼叫函式將會減少程式碼尺寸,然而,函式向量有受限的大小(在H8/300上最多128項,H8/300H上64項),而且和中斷向量共享空間。

為此選項,你必須使用2.7版或以後的GNU binutils中的GAS和GLD才能正確工作。

interrupt

在H8/300,H8/300H和SH上使用這個選項表明指定的函式是一箇中斷處理程式。當這個屬性存在時,編譯器將會生成函式入口和出口工序,為適應在中斷處理程式中的使用。

注意,H8/300,H8/300H和SH處理器的中斷處理程式可以通過interrupt_handler屬性來指定。

注意,在AVR上,中斷將會在函式裡面被啟用。

注意,在ARM上,你可以通過給中斷屬性新增一個可選的引數指定要處理的中斷型別,就像這樣:

void f () __attribute__ ((interrupt ("IRQ")));

這個引數的允許值是:IRQ, FIQ, SWI, ABORTUNDEF

interrupt_handler

在H8/300,H8/300H和SH上使用這個選項表明指定的函式是一箇中斷處理程式。當這個屬性存在時,編譯器將會生成函式入口和出口工序,為適應在中斷處理程式中的使用。

sp_switch

在SH上使用這個選項表明一個interrupt_handler函式應該切換到一個可替代的堆疊上。它期待一個字串引數,用來命名一個存放替代堆疊地址的全域性變數。

trap_exit

在SH上為interrupt_handle使用此選項來使用trapa而不是rte來返回。這個屬性期待一個整數引數來指定要使用的陷阱號。

eightbit_data

在H8/300和H8/300H上使用此選項來表明指定的變數應該放到8位元資料區。編譯器將會為在8位元資料區上的操作生成更高效的程式碼。注意8位元資料區的大小限制在256位元組。

tiny_data

在H8/300H上使用此選項來表明指定的變數應該放到微資料區。編譯器將會為在微資料區中存取資料生成更高效的程式碼。注意微資料區限制在稍微低於32K位元組。

signal

在AVR上使用此選項來表明指定的函式是一個訊號處理程式。當這個屬性存在時,編譯器將會生成函式入口和出口工序,為適應在訊號處理程式中的使用。在函式內部,中斷將會被遮蔽。

naked

在ARM或AVR移植上使用此選項來表明指定的函式不需要由編譯器來生成開場白/收場白工序。由程式設計師來提供這些工序。

model (model-name)

在M32R/D上使用這個屬性來設定物件和函式生成程式碼的可定址性。識別符號model-name是small,medium或large其中之一,各代表一種編碼模型。

small模型物件駐留在記憶體的低16MB中(所以它們的地址可以用ld24指令來載入),可用bl指令呼叫。

medium模型物件可能駐留在32位地址空間的任何地方(編譯器將會生成seth/add3指令來載入它們的地址),可用bl指令呼叫。

large模型物件可能駐留在32位地址空間的任何地方(編譯器將會生成seth/add3指令來載入它們的地址),而且可能使用bl指令夠不到(編譯器將會生成慢得多的seth/add3/jl指令序列)。

你可以在一個宣告中指定多重屬性,通過在雙圓括號中用逗號來把它們分割開,或者在一個屬性聲明後緊跟另一個屬性宣告。

一些人反對__attribute__特性,建議使用ISO C的#pragma來替代。在設計__attribute__時,有兩條原因不適合這麼做。

  1. 不可能從巨集中生成#pragma命令。
  2. 沒有有效說明同樣的#pragma在另一個編譯器中可能意味著什麼。

這兩條原因適用於幾乎任何提議#pragma的應用程式。為任何東西使用#pragma基本上都是一個錯誤。

ISO C99標準包括_Pragma,它現在允許從巨集中生成pragma。另外,#pragma GCC名字空間現在為GCC特定的pragma使用。然而,人們已經發現使用__attribute__來實現到相關宣告的自然的附件屬性很方便,而為構造而使用的#pragma GCC沒有自然地形成語法的一部分。檢視“C前處理器”中的“多種預處理命令”一部分。

這一段說明了在C語言中,使用到__attribute__的語法和屬性說明符繫結的概念。一些細節也許對C++和Objective C有所不同。由於對屬性不合適的語法,這裡描述的一些格式可能不能在所有情況下成功解析。

5.26節,宣告函式的屬性,瞭解施加於函式的屬性語義的細節。看5.33節,說明變數屬性,瞭解施加於變數的屬性語義的細節。看5.34節指定型別屬性,瞭解施加與結構體,共用體,和列舉型別的屬性語義的細節。

屬性說明符是的格式是:__attribute__((屬性列表))。屬性列表是一個可為空的由逗號分隔的屬性序列,其中的屬性型別如下:

空。空屬性會被忽略。 字(可能是一個識別符號如unused,或者一個保留字如const)。 在跟在後邊的圓括號中有屬性的引數的字。這些引數有如下的格式: 識別符號。比如,模式屬性使用這個格式。 跟有逗號或非空的由逗號分隔的表示式表。比如,格式化屬性使用這個格式。 可為空的由逗號分隔的表示式表。比如,格式化引數使用這個格式和一個單獨整型常量表達式列表,並且別名屬性使用這個格式和一個單獨的字串常量列表。

屬性說明符列表是一個由一個或多個屬性說明符組成的序列,不被任何其它標誌分開。

屬性說明符列表可能跟在一個標籤後的冒號出現,除了casedefault中的標籤。唯一的屬性使用在一個未使用的標籤後是合理的。這個特徵被設計成程式碼被包含而未使用的標籤但是在編譯是使用了"-Wall"引數。它也許不常用與人工編寫的程式碼中,雖然它應該在程式碼需要跳到一個包含在一段有#ifdef說明的條件編譯的程式段中很有用。

屬性說明符列表可以作為結構體、共用體或列舉說明符的一部分出現。它既可以直接跟在結構體、共用體或列舉說明符後,也可以緊貼大括號之後。如果結構體、共用體或列舉型別沒有在使用屬性說明符列表中用到的說明符定義,也就是說在如下這樣的用法中struct __attribute__((foo))沒有跟空的大括號,它會被忽略。在用到屬性說明符的地方,跟它靠近的大括號,它們會被認為和結構體、共用體或被定義的列舉型別有聯絡,不同任何出現在包含宣告的型別說明符,並且型別直到屬性說明符之後才結束。

否則,屬性說明符作為宣告的一部分出現,計算未命名引數和型別明宣告,並且和這個宣告相關(有可能內嵌在另外一個宣告中,例如在引數宣告的時候)。 將來屬性說明符在一些地方也任何用於特殊宣告符不超出代替宣告;這些情況將在一下提到。在屬性說明符被用於在函式或陣列中說明引數的地方,它也許用於函式 或陣列而不是指向一個會被隱含轉換的引數的指標,但這不僅是正確的工具。

任何在可能包含屬性說明符的宣告開始處的說明符與修飾符列表,抑或沒有這樣一個列表也許在上下文包含儲存類說明符。(一些屬性儘管本質自然是型別說 明符,並且僅在需要使用儲存類說明符的地方是合理的,例如section。)對這個語法有一個必要的限制:第一,在函式定義中的老式風格的引數宣告無法有 屬性說明符開頭,這種情況尚不提供)。在一些其它情況下,屬性說明符被允許使用這種語法但不被編譯器所支援。所有的屬性說明符在這裡被當做正割宣告的一部 分。在逐步被廢棄的在int型別預設省略了型別說明符的用法的地方,這樣的說明符和修飾符列表可能是沒有任何其它說明符或修飾符的屬性說明符列表。

在不止一個識別符號使用單獨的說明符或修飾符的宣告中的由逗號分隔的說明符列表中,屬性說明符列表可能直接在一個說明符之前出現(第一個除外)。目 前,這樣的屬性說明符不僅適用於被出現在自己的宣告中的識別符號,也可以用於在此宣告明中此後宣告的所有識別符號,但是將來它們可能只能用於那樣的單獨的標識 符。例如:__attribute__((noreturn)) void d0 (void), __attribute__((format(printf, 1, 2))) d1 (const char *, ...), d2 (void)

無返回值屬性用於所有的函式宣告中;格式化屬性會只用於d1,但是目前也用於d2(並且由此造成錯誤)。

屬性說明符列表可能直接出現在逗號、等號或分號之前,終止識別符號的說明除過函式定義。目前,這樣的屬性說明符用於已宣告的物件或函式,但是將來它們將附屬與相鄰的最遠的說明符。在簡單情況下沒有區別,但是例如在void (****f)(void) __attribute__((noreturn))這 樣的宣告中,當前無返回值的屬性用於f,這就會造成從f起不是一個函式的警告,但是將來它也許用於函式****f。在這種情況中的屬性的明確的語言符號將 不用於定義。在物件或函式的彙編名稱處被說明(看5.37節控制用於彙編程式碼的名稱),當前,屬性必須跟隨與asm說明之後;將來,在asm說明之前的屬 性可能用於鄰近的宣告符,並且它那些在它之的被用於已宣告的物件或函式。

將來,屬性說明符可能被允許在函式定義的宣告符後出現(在任意老式風格引數宣告或函式題之前)。

屬性說明符列表可能出現在內嵌宣告符的開始。目前,這種用法有一些限制:屬性用於在這個宣告中宣告過的識別符號和所有之後的宣告過的識別符號(如果它包 括一個逗號分隔的宣告符列表),而不僅是用於特定的宣告符。當屬性說明符跟在指標宣告符“*”之後時,它們應該出現在任意一種修飾符序列之後,並且不能和 它們混在一起。接下來的陳述預期將來的語言符號僅使這種語法更有用。如果你對標準ISO C的宣告符的說明格式熟悉的話,它將更好理解。

考慮T D1這樣的宣告(像C99標準6.7.5第四段中的子句),T包含宣告說明符的地方說明一個Type型別(比如int)並且D1是一個包含識別符號的標誌的宣告符。型別位標誌被說明用來匯出型別不包括在屬性說明符中的宣告符就像標準ISO C那樣。

如果D1有(屬性說明符列表 D)這樣的格式,並且T D這樣的宣告為標誌說明了“匯出宣告符型別列表 型別”的型別,這樣T D1為標誌說明了“匯出宣告符型別列表 屬性說明符列表 型別”的型別。

如果D1有 * 型別修飾符和屬性說明符列表 D這樣的格式,並且T D這樣的宣告為標誌說明“匯出宣告符型別列表 型別”的型別,則T D1為標誌說明“匯出宣告符列表 型別修飾符和屬性說明符列表 型別”的型別。

例如,void (__attribute__((noreturn)) ****f)();說明“指向返回空的無返回值函式的指標的指標的指標”的型別。作為另外的例子,char *__attribute__((aligned(8))) *f;說明“指向8位寬度的指向char型資料的指標的指標”的型別。再次注意這個陳述是被期待將來的語法符號,不是當前實現的。

GNU C對ISO C到允許函式原型忽略一個新的老式風格的無原型定義。考慮一下的例子:

/* 除非老式的編譯器才使用原型*/

/* Use prototypes unless the compiler is old-fashioned.  */
#ifdef __STDC__
#define P(x) x
#else
#define P(x) ()
#endif

/* 原型函式宣告.  */
int isroot P((uid_t));

/* 老式風格的函式定義.  */
int
isroot (x)   /* ??? lossage here ??? */
     uid_t x;
{
  return x == 0;
}

設想型別uid_t恰好是短整型。ISO C是決不允許這個例子的,因為在老式風格中的引數子字被提升了。因此在這個例子中,函式定義的引數確實是個和原型引數的短整型型別不匹配的整型。

ISO C的這個限制是它難以寫出可以移植到傳統C編譯器上的程式碼,因為程式設計師不知道uit_t型別是短整型、整型還是長整型。因此, 像GNU C允許在這些情況下原型忽略新的老式風格的定義。更嚴謹的是在GNU C中,函式原型引數型別如果一個錢型別想後來的型別在提升以前一樣,則忽略更新的老式風格定義說明的引數。因此在GNU C中上面這些個例子等價與下面的例子:

int isroot (uid_t);

int
isroot (uid_t x)
{
  return x == 0;
}

GNU C++ 不支援老式風格函式定義,故這個擴充套件和它是不相關的。

在GNU C當中,你可以使用C++風格的註釋,就是一"//"開頭並且一直到本行末。許多其它的C實現方案允許這樣的註釋,並且它們可能成為將來的C標準。但是,C++風格註釋在你說明了"-ansi",或"-std"選項來宣告使用ISO C在C99之前的版本時,將不會被識別,或者"-traditional"選項,因為它們和傳統的被這樣的//*註釋*/分隔符分隔的結構不相容。

在GNU C當中,你可以一般的在識別符號名稱中使用美元符。這是因為許多傳統的C實現方案允許這樣的識別符號。但是,識別符號中的美元符在少量目標機器不被支援,典型原因是目標彙編器不允許它們。

你可以在一個字串或字元常量中使用序列'/e'來表示ASCII字元ESC

關鍵字__alignof__允許你詢問一個物件如何對齊,或者一個型別的需要的最小對齊。它的語法很像sizeof

例如,不過目標機器需要一個雙精度值來使一個8位的邊界對齊,這樣__alignof__(double)就是8.在許多RISC機器上就是這樣的。在很多傳統的機器設計,__alignof__(double)是4或者甚至是2.

一些機器實際上從不需要對齊;它們允許參考任意資料型別甚至在一個奇數地址上。對這些機器,__alignof__報告型別的建議對齊。

__alignof__的運算元是一個左值而不是一個型別時,這個值是這個左值已知有的最大對齊。它可能由於它的資料類有而有這個對齊,或者因為它是結構體的一部分並且從那個結構體繼承了對齊。例如在這個宣告之後:

struct foo { int x; char y; } foo1;

__alignof__(foo1.y)的值可能是2或4,同__alignof__(int)相同,即使foo1.y的資料型別自己不需要任何對齊。

這是要求一個不完全的型別的對齊的錯誤。

一個使你說明一個物件對齊的關聯特徵是__attribute__ ((aligned (alignment)));請看下節。

關鍵字__attribute__允許你說明變數或結構體域的特殊屬性。這個關鍵字是跟有括在一對圓括號中的屬性說明。現在給變數定義了八個屬性:aligned, mode, nocommon, packed, section, transparent_union, unused,和weak。在特定的目標機器上定義了為變數定義了一些其它屬性。其它屬性可以用於函式(見5.26節 宣告函式屬性)和型別(見5.34節指定型別屬性)。其它q前端和末端可能定義更多的屬性(見C++語言的擴充套件章節)。

你可能也說明屬性有‘__’開頭並且跟在每一個關鍵字後邊。這允許你在標頭檔案中使用它們而不必擔心可能有同名的巨集。例如,你可以使用__aligned__來代替aligned

見5.27節屬性語法,瞭解正確使用屬性語法的細節。

aligned (對齊) 這個屬性以位元組為單位說明一個變數或結構體域的最小對齊。例如,這個宣告: int x __attribute__ ((aligned (16))) = 0; 造成編譯器在一個16位元組的邊界上分配全域性變數x。在68040上,這可以用在和一個彙編表示式連線去訪問需要16位元組對齊物件的move16指令。 你也可以說明結構體域的對齊。例如,建立一個雙字對齊的整型對,你可以寫: struct foo { int x[2] __attribute__ ((aligned (8))); }; 這是建立一個有兩個成員的共用體強制共用體雙字對齊的一個替換用法。 它可能不能說明函式的;函式的對齊是被機器的需求所決定且不能被改變的。你不能說明一個typedef定義的名稱的對齊因為這個名字僅僅是個別名,而不是特定的型別。 在前邊的例子,你可以明確說明你希望編譯器以給定的變數或結構體域對齊(以位元組位單位)。另外一種方案,你可以省略對齊因子並且進讓編譯器以你為之編譯的目標機器的最大有用對齊來對齊變數或域。例如,你可以寫: short array[3] __attribute__ ((aligned)); 無論何時你在一個要對齊的屬性說明中省略對齊因子,編譯器都會自動為宣告的變數或域設定對齊到你為之編譯的目標機器上對 曾經對任意資料型別用過的最大對齊。這樣做可以經常可以是複製動作更有效,因為編譯器可以使用任何指令複製最大的記憶體塊當執行復制到你要求這樣對齊的變數 或域中或從這從中複製時。 對齊屬性可以僅僅增加對齊;但是你可以通過也說明packed屬性。見下邊。 注意這對齊屬性的可行性可能被你的聯結器的固有限制所限制。在許多系統上,聯結器僅僅可以把變數整理和對齊到一個特定的最大對齊。(對一些聯結器,所支援的最大對齊可能非常非常小。)如果你的聯結器幾近可以對齊變數最大8位元組,那麼在__attribute__中說明aligned(16)仍然值提供給你8位元組對齊。從你的聯結器文件中可以獲得更多的資訊。 mode (模式) 這個屬性位宣告說明資料型別──任何型別都符合mode模式。這有效地使你獲取一個整型或浮點型的符合寬度。 你可能也說明'byte'或'__byte__'模式來表示模式同一位元組的整數一致,'word'或'__word'來表示一個字的整數的模式,並且'pointer'或'__pointer__'來表示指標的模式。

nocommon

這個屬性說明要求GCC不要把放置一個變數為 "common"而是直接給它分配空間。如果你說明'-fno-common'標誌,GCC將對所有變數這樣做。 給變數說明nocommon屬性則提供初值為零。變數可能僅在一個原始檔中初始化。 packed packed屬性說明一個變數或結構體域應該有儘可能小的對齊──一個變數一位元組或一個域一位元組,除非你和對齊屬性一起說明了一個更大的值。 這裡是一個域xpacked屬性說明的結構體,所以它直接跟在a之後:
struct foo
{
  char a;
  int x[2] __attribute__ ((packed));
};
section("段名") 通常,編譯器將把物件放置在生成它的段中想databss。但是有時候你學要附加段,或者你需要一定特定的變數去出現在特殊的段中,例如去對映特殊的硬體。section屬性說明一個變數(或函式)存在一個特定的段中。例如,這個小程式使用一些說明段名:
struct duart a __attribute__ ((section ("DUART_A"))) = { 0 };
struct duart b __attribute__ ((section ("DUART_B"))) = { 0 };
char stack[10000] __attribute__ ((section ("STACK"))) = { 0 };
int init_data __attribute__ ((section ("INITDATA"))) = 0;

main()
{
  /* Initialize stack pointer */
  init_sp (stack + sizeof (stack));

  /* Initialize initialized data */
  memcpy (&init_data, &data, &edata - &data);

  /* Turn on the serial ports */
  init_duart (&a);
  init_duart (&b);
}
和一個全域性變數的初始化定義一起使用section屬性,就像例子中那樣。GCC給出一個警告或者在未初始的變數宣告中忽略section屬性。 你可能由於聯結器的工作方式僅同一個完全初始化全域性定義使用section屬性。聯結器要求每個物件被定義一次,例外的是未初始化變數假定竟如common(或bss)段而且可以多重“定義”。你可以強制一個變數帶'-fno-common'標誌初始化或nocommon屬性。 一些檔案格式不支援隨意的段,所以section屬性不全適用於所有的平臺。如果你需要對映一個完全滿足的模組到一個特定的段,慎重考慮使用聯結器的裝置來代替。 在Windows NT上, 在命名的段附加放置變數定義,這段也可以個在所有執行的可執行檔案或DLL檔案之間共享。例如,這個小程式通過將其放入命名的段並將該段標記位共享而定義了共享資料。 你僅可以在section屬性完全初始化全域性定義是使用shared屬性,因為聯結器的工作方式。看section屬性來獲得更多的資訊。 shared屬性僅適用於Windows NT。 這個屬性附屬與一個共用體型的函式引數,意味著引數可能具有與共用體成員一致的任何型別,但是引數被當做共用的第一個成 員的型別傳送。看5.34節說明型別屬性瞭解更多細節。你也能吧這個屬性用在對共用體資料型別適用typedef是;這樣它就可以被用在所有這個型別的函 數引數上。 unused 變數有這個屬性,意味著這個變數可能沒有被使用。GCC將不對這個變數產生警告。 weak weak屬性在5.26節宣告函式屬性已經被陳述過。 model(模型名) 在M32R/D上使用這個屬性去設定物件的編址能力。這個識別符號模型名是small,medium或large中的一個,代表每一個程式碼模型。 小模型物件存在與低16MB記憶體中(所以它們的地址可以和一個ld24指令一起裝入)。 中和大模型物件可能存在任何一個32位的地址空間中(編譯器將形成seth/add3指令來裝入它們的地址)。

說明多個屬性用逗號吧它們分隔開寫在一對圓括號中:例如,'__attribute__ ((aligned (16), packed))'

當你定義結構體和共用體型別時,關鍵字attribute允許你為這些型別指定特殊的屬性。這個關鍵字後面跟隨著包含雙parentheses的指定型別。四中屬性常被定義為:對齊(aligned),封裝(packed)型,透明共用體型(transparent-union)和未使用。另外的屬性則被定義為函式(看5.26段函式屬性的宣告)和變數(看5.33段指定變數屬性)。

你可以指定這些屬性在關鍵字之前或後面。這就使你能在標頭檔案應用這種屬性而不必宣告 可能有同樣名字的巨集 例如:你能用_aligned__ instead of aligned.

你可以在括號中放入列舉的定義或宣告, 結構或共用型別的定義和集裝屬性,括號後指定其屬性。

你也能在列舉,結構或共用間指定屬性的tag和名字而不是在)後。

5.27 屬性語法,對於準確使用語法屬性

aligned 這種屬性指定一個最小的佇列(按位算)為變數指定型別。例如,如下的宣告: 強制使編譯器確定每個型別為S的結構體變數或者更多的組合整型,將被分配和匹配為至少8位。在可精簡效能結構中,當複製一個結構體S變數到另外一個時。擁有所有的結構體S 對齊8位的變數使編譯器能使用lddstd,因此可以提高執行效率。 {注意,任何給定的結構體或共同體型別的對齊是ISO C標準所需的,至少是正在談論的結構體或共同體型別所有成員對齊的最小公倍數的一個完全的倍數。這就意為著你能有效的教正附屬於aligen對於結構體和共用體佇列成員的屬性。但是以上例子中插入的註釋更加明顯,直觀和可讀對於編譯者來校正一個完全的結構體或共用體型別組合。 封裝(packed) 這種屬性接近於列舉,結構或者共用型別的定義,指定一個所需最小的記憶體用來代辦這種型別。 為結構體和共用體指定這種屬性等效於為他們的每個成員指定集裝的的屬性。指定“短-列舉”標誌等同於指定封裝的屬性在所有的列舉定義。 你也可以僅僅在括號後面指定這種屬性,而不是為他定義型別的宣告,除非宣告也包括列舉的定義。 transparent_union 這種屬性基於共用體的定義,表明一些函式的引數使用共用型別會被看作呼叫函式的一種特殊途徑。 首先,引數與同透明共用體型別保持一致,能成為任何型別的共用體;不需要轉換. 加入共用體包含指標型別,一致的引數能成為一個空指標常量或空指標表示式;加入共用體包含空指標,一致引數能成為指標表示式。加入共用體成員型別是之至, 型別修飾符就像指定,就如一般的指標的轉換一樣。 第二,引數被傳給函式用到的呼叫轉換(透明共用體的第一個成員,不能呼叫轉換共用體本身。所有的成員必須擁有相同的機器代理;這就使引數傳輸能進行了。 透明共用體擁有多種介面庫函式來處理相容性問題。例如,假定等待函式必須要接受一種整型來遵從Posix,或者一個共用wait函式要適應4.1BSD介面。記入wait函式的引數是空型,wait函式將接受各種型的引數,但是它也能接受另外的指標型和這個將能使引數型別的檢測降低效用。而為"sys/wait.h"定義的介面如下: 這種介面允許整形或共用體等待引數的傳輸,用整型的呼叫轉換,這個程式能呼叫引數和型別:
int w1 () { int w; return wait (&w); }
int w2 () { union wait w; return wait (&w); }
在這個介面下,wait的執行將是這樣:
pid_t wait (wait_status_ptr_t p)
{
  return waitpid (-1, p.__ip, 0);
}
未用(unused) 當應用一種型別(包括共用體和結構體),這個屬性意為著這種變數型別可能出席那不能使用的,GCC講不能產生警告對這些型別的變數,幾十變數什麼作用都沒,這還經常能導致鎖或執行緒的種類(通常被定義但不引用,但是包含構建與消毀都存在非平凡簿記功能.)

通過宣告一個行內函數,你就可以直接用GCC把函式原始碼和呼叫它的函式原始碼合成起來。這樣,通過消除高層的函式呼叫使得函式執行更快;另外,如 果任何的實參是常數,它們的已知值可能允許簡化從而不使所有的行內函數程式碼在編譯時被包含進來。這對程式碼大小的影響幾乎是不可預知的;和行內函數相比,結 果程式碼的大或小取決於具體情況。函式內聯是一種優化,而且只在優化編譯上起作用。如果你沒有用'-0',那就沒有函式是真正內聯的。

在C99標準裡包含行內函數,但是,當前,GCC的實現和C99的標準要求確實存在差異。

像這樣,在函式聲明裡用內聯的關鍵字可以宣告一個函式內聯:

inline int
inc (int *a)
{
  (*a)++;
}

(如果你正在寫一個要被包含在一個標準C程式的標頭檔案,請用 __inline__ 代替 inline.參見5.39節 備用關鍵字。)你同樣可以通過加選項`-finline-functions'使得所有足夠簡單的程式內聯。

注意,在函式定義中的固定用法可以使函式不適合做內聯。這些用法有:varargs函式的使用 ,alloca函式的使用, 可變大小資料型別的使用(參見5.14節 變長陣列),goto 計算的使用 (參見 5.3節 可賦值標籤), 非區域性goto語句的使用, 以及巢狀函式的使用(參見 5.4節巢狀函式 ).用`-Winline'可以在一個函式被標記為內聯而不能被取代時出現警告,並給出錯誤原因。

注意在C和Objective C中, 不象C++那樣, 內聯關鍵字不會影響函式的聯接。

GCC會自動將定義在C++程式內class body中的元函式內聯即使它們沒有被明確的宣告為內聯。(你可以用`-fno-default-inline'忽略它;參見選項控制C++語法。)Options Controlling C++ Dialect

當一個函式同時是靜態和內聯時,如果所有對這個函式的呼叫都被綜合在呼叫者中,而且函式地址從沒有被使用過,函式所有的彙編程式碼都沒有被引用過。在這種情況下,GCC事實上不會為這個函式輸出彙編程式碼,除非你指定選項`-fkeep-inline-functions'。由於各種原因一些函式呼叫不會被綜合(特殊的,呼叫在函式定義之前和在函式中的遞迴呼叫是不能被綜合的)。如果有一個非綜合呼叫,函式會被像平常一樣編譯出彙編程式碼。如果程式引用了它的地址,這個函式也必須像平常一樣被編譯,因為地址是不能被內聯的。

當一個函式不是靜態時,編譯器會假定在其它原始檔中可能存在呼叫;由於在任何程式中全域性符號(全域性變數)只能被定義一次,函式一定不能在其它原始檔中被定義,所以在那裡的呼叫是不能被綜合的。因此,通常一個非行內函數總是被獨立的編譯。

如果你在函式定義中同時指定內聯和外部援引,那麼這個定義只會被用作內聯。即使沒有明確的引用它的地址,函式也決不會被獨立編譯。這些地址變成了外部援引,就好像你只是聲明瞭函式,沒有定義它一樣。

這種內聯和外部援引的結合和巨集定義的效果差不多。這種結合的用法是把函式定義和這些關鍵字放在一個頭檔案中,把另外一份定義(缺少內聯和外部援引) 的拷貝放在庫檔案中。標頭檔案中的定義會使對這個函式的大多數呼叫成為內聯。如果還有其它函式要用,它們將會查閱庫檔案中專門的拷貝檔案。

為了將來當 GCC 實現 C99 標準語法中的行內函數時有相容性,僅使用靜態內聯是最好的。(當`-std=gnu89'被指明時,當前語法可以保留可用部分,但最後的預設將會是`-std=gnu99',並且它將會實現C99語法,儘管現在他並沒有被這樣做。)

GCC沒有優化是沒有內聯任何函式。內聯好還是不好並不明確,既然這樣,但是我們發現沒有優化時正確執行是很困難的。所以我們做簡單的事,避開它。

在彙編指令中用匯編語言,你可以指定用C語言中的表示式的運算元。這就意味著你不需要猜測哪個暫存器或儲存單元中包含你想要用的資料。

你必須指定一個儘可能像機器描述中的彙編指令模組,為每個運算元加上一個被約束排成一列的運算元。

這是怎樣使用68881的 fsinx指令的例子:

asm ("fsinx %1,%0" : "=f" (result) : "f" (angle));

這裡angle是一個用來輸入運算元的C表示式,result是輸出運算元。每個都有`"f"'作為它的運算元約束,說明需要一個浮點暫存器。`=f' 中的`='指明瞭這個運算元是一個輸出;所有輸出運算元的被約束使用`='。這種約束在同語言中被用於機器描述(參見20.7節 運算元約束)。

每個運算元在插入語中被一個後面跟著C表示式的約束運算元字串描述。一個冒號隔開了彙編程式模組和第一個輸出運算元,另一個隔開了最後一個輸出操 作數和第一個輸入運算元,即便要的話。逗號在每個群中隔開了不同的運算元。運算元的總數被限制在10或者被限制在運算元的最大數字,在任何機器描述中的任 何指令模型,無論哪一個都較大。

如果只有輸入運算元而沒有輸出運算元,你應該在輸出運算元在的位置的兩頭放兩個連續的冒號。

輸出運算元表示式必須是一個左值;編譯器可以檢測這點。輸入運算元不需要是左值。編譯器不能檢測運算元的資料型別對指令執行來說是否合理。它不能解 析彙編指令模組,它也不知道彙編指令的意思。甚至不能判斷它是否是一個有效的彙編輸入。擴充套件彙編的特徵是多數情況下用於機器指令,而編譯器本身卻不知道它 的存在。如果輸出表達式不可能是直接地址(比如說,它是一個位域),你的約束必須允許一個暫存器。那樣的話,GCC將會把暫存器當作彙編程式的輸出,接下 來儲存暫存器內容用作輸出。

普通的輸出運算元必須是隻讀的;GCC 會假定這些在指令之前運算元中的左值是死的,並且不需要產生。擴充套件彙編支援輸入-輸出或讀-寫運算元。用字元`+'可以指出這種運算元並且在輸出運算元中列出。

當對一個讀寫運算元(或是運算元中只有幾位可以被改變)的約束允許一個而中選一的暫存器,你可以從邏輯上把它的作用分成兩個運算元,一個是輸出操作 數和一個只寫輸出運算元。他們之間的關係是在指令執行時,被約束表示出他們需要在同一個位置。你可以對兩個運算元用同樣的C語言表示或不同的表示。這裡我 們寫了一個結合指令(假想的)用後備地址暫存器當作它的只讀資源運算元,把foo作為它的讀寫目的地。

asm ("combine %2,%0" : "=r" (foo) : "0" (foo), "g" (bar));

對運算元1來說,`"0"'約束是指它必須佔據相同的位置相運算元0一樣。在約束中一個阿拉伯數字只被允許出現在輸入運算元中,而且,它必須提及到一個輸出運算元。

在約束中只有一個阿拉伯數字可以保證一個運算元會和其它運算元一樣出現在同一個地方。起碼的事實,foo是兩個運算元的確切值並不足以保證在產生的彙編程式碼中它們會出現在相同的位置。下面的程式碼是不可靠的:

asm ("combine %2,%0" : "=r" (foo) : "r" (foo), "g" (bar));

各種優化或重新裝載可以使運算元0和1在不同的暫存器中;GCC 知道沒有理由不這樣做。舉例來說,編譯器可能會找到一個暫存器中foo值得拷貝並用它作為運算元1,但是產生的輸出運算元0卻在另外一個暫存器中(後來拷 貝到foo自己的地址裡)。當然,由於用於運算元1的暫存器在彙編碼中甚至沒有被提及,就不會產生結果。但GCC卻不能指出來。

一些頻繁使用的指令指定了硬裝置暫存器。為了描述這些,在輸入運算元之後寫上第三個冒號,後面緊跟著頻繁使用的硬裝置暫存器的名字(以串的形式給出)。這又一個現實的例子:

asm volatile ("movc3 %0,%1,%2"
              : /* no outputs */
              : "g" (from), "g" (to), "g" (count)
              : "r0", "r1", "r2", "r3", "r4", "r5");

你不可能通過用一個輸入運算元或一個輸出運算元交迭的方式來描述一個頻繁使用的硬裝置暫存器。舉個例子,如果你在快表中提到一個暫存器,你就不可能 用一個運算元描述一個有一個成員的暫存器組。你沒有辦法去指定一個輸入運算元沒有同時指定為輸出運算元時被修正。注意如果你指定所有輸出運算元都出於這個 目的(而且因此沒有被使用),你就需要去指定可變的彙編程式碼構造,像下面說得那樣,去阻止GCC刪除那些沒有被用到的彙編程式碼段。

如果你從彙編程式碼中找到一個特殊的暫存器,你大概不得不在第三個冒號列出之後這個暫存器來告訴編譯器暫存器的值是修正值。在一些彙編程式中,暫存器的名字以`%'開始;要在彙編程式碼中生成一個‘%’,你必須在輸入時這樣寫:`%%'。

如果你的彙編指令可以改變條件碼暫存器,在頻繁使用的暫存器列表中加上`cc'。GCC在一些機器上表現條件碼像制定硬裝置暫存器一樣;`cc'可以去命名這種暫存器。在其它機器上。條件碼被給於不同的操作,而且指定`cc'沒有效果。但是它在任何機器上總是有效的。

如果你的彙編指令通過不可預知的方式修正儲存器,在頻繁使用的暫存器列表中加上`memory'。這會使GCC通過彙編指令不把儲存器的值存入暫存器。如果儲存器沒有受影響,你也可以加上可變的沒有被列在彙編程式輸入輸出表上的關鍵字,就像`memory'的頻繁使用沒有被計算反而成為彙編程式的副