C語言中文網 目錄
首頁 > 編程筆記 > C語言筆記 閱讀:2,646

C語言#line、#error和#pragma命令,以及_pragma運算符

本文將介紹幾種預處理命令及其使用方法,其中包括 #line 命令、#error 命令和 #pragma 命令。此外,還講述了_Pragma 運算符的相關知識。

定義行號

編譯器會在警告消息、錯誤消息與調試信息中包含代碼所在的行號與所在的源文件名,并提供給調試工具。你可以在源代碼中利用 #line 命令改變編譯器默認指定的文件名與行號信息。#line 命令的語法如下:
#line line_number ["filename"]

#line 命令的下一行行號會指定為 line_number。如果該命令也包含可選字符串字面量"filename",那么該編譯器會把該字符串名稱作為當前源文件名。

line_number 必須是大于 0 的十進制常量。如下例所示:
#line 1200 "primary.c"

包含 #line 命令的那一行代碼,也可以包含其他宏。如果包含其他宏,預處理器會先展開所有宏,然后再執行 #line 命令。但要確保在宏展開后,#line 命令是正確的。

程序可以利用標準預定義宏 __LINE__ 和 __FILE__ 來訪問當前的行號和文件名設置
printf( "This message was printed by line %d in the file %s.\n",
        __LINE__, __FILE__ );

#line 命令通常用在將 C 源代碼作為輸出的程序上。通過將對應的輸入文件行號放置在 #line 命令內,程序可以讓 C 編譯器的錯誤消息指向源文件中相應的行。

生成錯誤消息

無論是否有實際錯誤,#error 命令都會讓預處理器發出錯誤消息。它的語法如下:
#error [text]

如果上述命令存在可選項 text,則 text 就會被包含在預處理器的錯誤消息中。然后,編譯器會終止處理源代碼,并結束執行,仿佛遇到了嚴重錯誤。text可以是任意預處理器記號序列。如果 text 中有其他宏,它們都不會被展開。最好在這里使用字符串字面量,以避免標點符號字符(如單引號)的影響。

下面的例子測試標準宏 __STDC__ 是否已經被定義。如果沒有,則生成一個錯誤消息:
#ifndef __STDC__
  #error "This compiler does not conform to the ANSI C standard."
#endif

#pragma 命令

#pragma 命令是向編譯器提供額外信息的標準方法,其格式如下:
#pragma [tokens]

如果 #pragma 之后的第一個標記(token)是 STDC,那么該命令就是一個標準 pragma。否則,該 #pragma 命令的作用取決于實現版本。為了保障代碼的可移植性,應該盡量少使用 #pragma 命令。

如果預處理器支持所指定的標記,就會執行這些標記所代表的動作,或者把該信息傳遞給編譯器。如果預處理器不支持所指定的標記,就忽略該 #pragma 命令。

例如,最新版本的 GNU C 編譯器和微軟 Visual C 編譯器都支持 #pragma pack(n),它使得編譯器讓結構成員對齊到特定的字節邊界。下面的例子使用 pack(1)指示每個結構成員必須對齊到字節邊界:
#if defined( __GNUC__ ) || defined( _MSC_VER )
  #pragma pack(1)                             // 對齊字節,沒有填充
#endif

單字節對齊方式可以確保結構成員之間不會有間隙。pack 的參數 n 通常是 2 的冪(但冪值較小)。例如,pack(2)把結構成員對齊到偶數地址,而 pack(4)把結構成員對齊到 4 為倍數的地址。pack()沒有參數,它指示對齊方式設置為實現版本的默認值。

C99 新增下面三個標準的 pragma:
#pragma STDC FP_CONTRACT on_off_switch
#pragma STDC FENV_ACCESS on_off_switch
#pragma STDC CX_LIMITED_RANGE on_off_switch
on_off_switch 的值必須是 ON、OFF 或 DEFAULT。

_Pragma 運算符

無法通過宏展開創建一個 #pragma 命令(或任何其他命令)。當碰到需要這么做的情況時,C99 新增了一個預處理運算符 _Pragma,它可以與宏配合使用。它的語法如下:
_Pragma (string_literal )

_Pragma 運算符的工作方式是這樣的。首先,該 string_literal 操作數會被“解字符串化”(destringized),或被轉換為預處理器記號序列,該過程如下:刪除字符串前后的雙引號;使用"替代 \";使用 \ 替代 \\。然后,預處理器會翻譯前述結果序列的記號,類似于 #pragma 命令。

下面這一行代碼定義了一個名為 STR 的宏,借此可以使用 _Pragma 運算符重寫 #pragma 命令:
#define STR(s)   #s             // 這個#是“字符串化”運算符

有了上述定義,下面兩行代碼是等同的:
#pragma tokens
_Pragma ( STR(tokens) )

下面的例子是在宏中使用 _Pragma 運算符:
#define ALIGNMENT(n) _Pragma( STR(pack(n)) )
ALIGNMENT(2)

宏替換會把調用宏 ALIGNMENT(2)的過程重寫為如下形式:
_Pragma( "pack(2)" )

預處理器接著處理這行代碼,實現方式與使用下面的命令一樣:
#pragma pack(2)

精美而實用的網站,提供C語言C++STLLinuxShellJavaGo語言等教程,以及socketGCCviSwing設計模式JSP等專題。

Copyright ?2011-2018 biancheng.net, 陜ICP備15000209號

底部Logo