翻譯階段
來自 cppreference.com
C 原始碼檔案由編譯器處理,就好像以下階段按此精確順序發生一樣。實際實現可以組合這些操作或以不同方式處理它們,只要行為相同即可。
目錄 |
[編輯] 階段 1
1) 原始碼檔案的各個位元組(通常是某種多位元組編碼(例如 UTF-8)的文字檔案)以實現定義的方式對映到源字元集的字元。特別地,依賴於作業系統的行尾指示符被替換為換行符。
- 源字元集是一個多位元組字元集,它包含基本源字元集作為單位元組子集,由以下 96 個字元組成
a) 5 個空白字元(空格、水平製表符、垂直製表符、換頁符、換行符)
b) 從 '0' 到 '9' 的 10 個數字字元
c) 從 'a' 到 'z' 以及從 'A' 到 'Z' 的 52 個字母
d) 29 個標點符號: _ { } [ ] # ( ) < > % : ; . ? * + - / ^ & | ~ ! = , \ " '
2) 三字元序列被替換為相應的單字元表示。(C23 之前)
[編輯] 階段 2
1) 每當反斜槓出現在行尾(緊跟在換行符之後)時,反斜槓和換行符都被刪除,將兩個物理源行合併成一個邏輯源行。這是一個單次透過操作:以兩個反斜槓結尾後跟一個空行的行不會將三行合併成一行。
執行此程式碼
#include <stdio.h> #define PUTS p\ u\ t\ s /* Line splicing is in phase 2 while macros * are tokenized in phase 3 and expanded in phase 4, * so the above is equivalent to #define PUTS puts */ int main(void) { /* Use line splicing to call puts */ PUT\ S\ ("Output ends here\\ 0Not printed" /* After line splicing, the remaining backslash * escapes the 0, ending the string early. */ ); }
2) 如果一個非空的原始檔在此步驟後不以換行符結尾(無論是最初沒有換行符,還是以反斜槓結尾),則行為是未定義的。
[編輯] 階段 3
1) 原始檔被分解為註釋、空白字元序列(空格、水平製表符、換行符、垂直製表符和換頁符)以及以下預處理記號
a) 標頭檔案名稱:<stdio.h> 或 "myfile.h"
b) 識別符號
e) 運算子和標點符號,例如 +、<<=、<% 或 ##。
f) 不屬於任何其他類別的單個非空白字元
2) 每個註釋都被一個空格字元替換
3) 換行符被保留,非換行符的空白序列是否可以合併為單個空格字元是實現定義的。
如果輸入已經解析到給定字元的預處理記號,則下一個預處理記號通常被認為是構成預處理記號的最長字元序列,即使這會導致後續分析失敗。這通常被稱為最大詞法分析。
int foo = 1; // int bar = 0xE+foo; // error: invalid preprocessing number 0xE+foo int bar = 0xE/*Comment expands to a space*/+foo; // OK: 0xE + foo int baz = 0xE + foo; // OK: 0xE + foo int pub = bar+++baz; // OK: bar++ + baz int ham = bar++-++baz; // OK: bar++ - ++baz // int qux = bar+++++baz; // error: bar++ ++ +baz, not bar++ + ++baz int qux = bar+++/*Saving comment*/++baz; // OK: bar++ + ++baz
最大詞法分析規則的唯一例外是
- 標頭檔案名稱預處理記號僅在 #include 或 #embed(C23 起) 指令中,在 __has_include 和 __has_embed 表示式中(C23 起) 以及在 #pragma 指令中實現定義的位置形成。
#define MACRO_1 1 #define MACRO_2 2 #define MACRO_3 3 #define MACRO_EXPR (MACRO_1 <MACRO_2> MACRO_3) // OK: <MACRO_2> is not a header-name
[編輯] 階段 4
1) 預處理器執行。
2) 每個透過 #include 指令引入的檔案都遞迴地經過階段 1 到 4。
3) 在此階段結束時,所有預處理指令都從原始碼中刪除。
[編輯] 階段 5
1) 字元常量和字串字面量中的所有字元和轉義序列都從源字元集轉換為執行字元集(可以是多位元組字元集,例如 UTF-8,只要階段 1 中列出的基本源字元集中的所有 96 個字元都有單位元組表示)。如果轉義序列指定的字元不是執行字元集的成員,則結果是實現定義的,但保證不是空(寬)字元。
注意:在此階段執行的轉換可以透過某些實現中的命令列選項進行控制:gcc 和 clang 使用 -finput-charset 指定源字元集的編碼,-fexec-charset 和 -fwide-exec-charset 指定沒有編碼字首的字串字面量和字元常量中執行字元集的編碼(C11 起)。
[編輯] 階段 6
相鄰的字串字面量被連線。
[編輯] 階段 7
編譯發生:記號被語法和語義分析,並作為翻譯單元進行翻譯。
[編輯] 階段 8
連結發生:翻譯單元和滿足外部引用所需的庫元件被收集到一個程式映像中,該映像包含在執行環境中(作業系統)執行所需的資訊。
[編輯] 參考文獻
- C23 標準 (ISO/IEC 9899:2024)
- 5.1.1.2 翻譯階段 (p: 待定)
- 5.2.1 字元集 (p: 待定)
- 6.4 詞法元素 (p: 待定)
- C17 標準 (ISO/IEC 9899:2018)
- 5.1.1.2 翻譯階段 (p: 9-10)
- 5.2.1 字元集 (p: 17)
- 6.4 詞法元素 (p: 41-54)
- C11 標準 (ISO/IEC 9899:2011)
- 5.1.1.2 翻譯階段 (p: 10-11)
- 5.2.1 字元集 (p: 22-24)
- 6.4 詞法元素 (p: 57-75)
- C99 標準 (ISO/IEC 9899:1999)
- 5.1.1.2 翻譯階段 (p: 9-10)
- 5.2.1 字元集 (p: 17-19)
- 6.4 詞法元素 (p: 49-66)
- C89/C90 標準 (ISO/IEC 9899:1990)
- 2.1.1.2 翻譯階段
- 2.2.1 字元集
- 3.1 詞法元素
[編輯] 另請參閱
C++ 文件中的翻譯階段
|