最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

【C/C++】在預(yù)處理之前,編譯器做了什么事情?- 翻譯階段1&2

2023-03-31 21:07 作者:冒-_-泡  | 我要投稿

很多資料會告訴你,C或C++的編譯過程包括預(yù)處理、編譯、鏈接三個(gè)步驟,不過實(shí)際規(guī)定要更復(fù)雜一些,例如上篇提到的通用字符處理,就是在預(yù)處理之前

C或C++為代碼的編譯(廣義概念)定義了一整套流程,稱為翻譯階段(translation phases),由于具體到這個(gè)語言,編譯(狹義概念)一般被認(rèn)為是預(yù)處理和鏈接之間的事情,這里特別用了翻譯一詞,流程是從處理代碼文本開始,從設(shè)備上讀取源文件的IO流程不算在內(nèi)

翻譯分多個(gè)階段,每個(gè)階段負(fù)責(zé)處理相對獨(dú)立的流程:

  • 1:字符相關(guān)

  • 2:行相關(guān)

  • 3:為預(yù)處理做準(zhǔn)備的詞法分割

  • 4:預(yù)處理(即#define、#include之類)

  • 5&6:處理字符串字面量(編碼、字面量連接等)

  • 7:編譯

  • 8:C鏈接;C++模板實(shí)例化

  • 9:C++鏈接

C語言有8個(gè)階段,C++則是9個(gè),之所以多一個(gè)是因?yàn)镃++的模板實(shí)例化流程被獨(dú)立出來了(不過一些具體實(shí)現(xiàn)也會和編譯階段混在一起)

參考:https://zh.cppreference.com/w/cpp/language/translation_phases

從第4階段開始,就是我們比較熟悉的流程了,而第3階段和預(yù)處理有關(guān),且較為復(fù)雜,其中一些內(nèi)容可以獨(dú)立來說,所以先主要講講1和2階段做了什么事情

階段1

字符處理流程在上期已經(jīng)提到了一部分,或者可以說是一大部分,除了通用字符名(\u或\U那些)和具體編譯器擴(kuò)展的字符功能(直接寫漢字也算),還做了兩件小事

一個(gè)是行尾指示符都被替換為標(biāo)準(zhǔn)的換行符,這里的“行尾指示符”是和操作系統(tǒng)相關(guān),其實(shí)就是我們熟知的“\r\n”(dos/win)、“\n”(*nix)、“\r”(老mac),其中單獨(dú)的“\r”目前已經(jīng)被mac廢棄了,所以可以說就是dos和unix兩種風(fēng)格(也叫CRLF和LF),如果按書面意思,就是根據(jù)所在平臺,將其統(tǒng)一換成“\n”

不過實(shí)際測試的話,會發(fā)現(xiàn)即便在linux下,很多編譯器也會將行尾的“\r\n”替換為“\n”,可以看做是一種兼容行為了,然而,這往往是一種編譯器自行的擴(kuò)展實(shí)現(xiàn),不同編譯器的處理可能是有差別的

(可能有人說,回車和換行都是空白字符,C和C++又不是換行敏感的,這里的差別會體現(xiàn)出來嗎?有一個(gè)地方會體現(xiàn),就是原始字符串,這個(gè)以后再細(xì)講)

另一個(gè)字符處理是三標(biāo)符序列,如果代碼中存在“??”開頭的三標(biāo)符,則替換其實(shí)際對應(yīng)的符號,對應(yīng)關(guān)系:

這一點(diǎn)是因?yàn)樵缙谧址膯栴},不過隨著時(shí)代發(fā)展,這個(gè)語法在C++17開始已經(jīng)廢棄了,而在老的C++標(biāo)準(zhǔn)下,很多編譯器也有默認(rèn)關(guān)閉的情況,如果在老標(biāo)準(zhǔn)下寫代碼,或維護(hù)很老的代碼,則需要注意一下

注意三標(biāo)符的替換是在階段1,是一個(gè)非常早期、簡單粗暴的處理,這跟字符串中的轉(zhuǎn)義不是一回事,和\u\U一樣,它不只能用于字符串中,而是在正常代碼中來表示被替換的字符

那么假如你的代碼開發(fā)中是開啟了這個(gè)功能,而你又想在字符串中使用三標(biāo)符的原始樣子該怎么辦呢,兩個(gè)辦法:字符串字面量連接,和問號本身的轉(zhuǎn)義:

前者是利用了翻譯階段6做的事情:相鄰的字符串字面量會被連接在一起,從而在書面上規(guī)避了“??=”連寫的情況,繞開了階段1的這個(gè)處理,后者則可以說是C專門為問號準(zhǔn)備的字符串中的轉(zhuǎn)義符“\?”,其代表的就是問號本身,而唯一的作用就是在這里繞過階段1的此處理

顯然,由于階段1并不解析字符串(解析字符串必須在詞法階段才行),以下寫法是不行的:


階段2

這個(gè)階段做了兩件事情,咱們先說其中簡單的工作:如果一個(gè)源碼文件(.h或.c或.cpp等)不是以換行符結(jié)尾,那么補(bǔ)上一個(gè)換行(當(dāng)然并不會修改源文件,只是視為其有一個(gè)換行)

這可能是為了避免頭文件被包含后,由于包含進(jìn)來的文件末尾沒有換行,可能會和后繼的內(nèi)容組成奇怪結(jié)果的一種規(guī)避措施

另外一件工作則是這個(gè)階段處理的大頭:如果一行是反斜杠結(jié)尾,則刪除這個(gè)反斜杠和其后的換行。也就是說,將兩行連接在一起,例:

很多人都知道C代碼可以用反斜杠來折行,但估計(jì)不少初學(xué)者沒想到的是,這個(gè)處理如此簡單粗暴,整個(gè)流程可以就一行Python代碼搞定:text.replace("\\\n", ""),就是普通的文本處理,連詞法分析都沒到

而在其他一些語言中,雖然支持反斜杠折行,但可能是放在編譯階段的,例如Python中:

顯然Python是在做詞法分析的時(shí)候處理這個(gè)的

這個(gè)替換流程是單趟操作,每次替換后,是從替換的位置開始繼續(xù)向后處理,而并不會管替換本身產(chǎn)生的新的行尾反斜杠,例:

如果單看這個(gè)階段本身是非常簡單的,但是考慮到它是在階段1之后進(jìn)行,前后聯(lián)系起來就有一些有意思的場景:

在階段1,這個(gè)代碼中的“??/”會被替換為反斜杠,然后在階段2,第一行行尾是反斜杠,所以處理后變成了:

同樣的道理,用這個(gè)方式折行的時(shí)候,需要保證反斜杠一定在行尾,原則上說,后面多一個(gè)空格都不行,不過GNUC對于這種情況,會給出一個(gè)警告然后忽略后面的空白符,還是當(dāng)做折行來處理,是一個(gè)語法擴(kuò)展

另外,“\\\r”在有的編譯器中也會被視為折行連接,即便“\r”并非一個(gè)應(yīng)該出現(xiàn)在代碼文本中的字符,關(guān)于“\r”可能引發(fā)的問題在后面討論原始字符串時(shí)也會提到

從階段1的規(guī)定,我們知道可以用通用字符的轉(zhuǎn)義,那么如果覺得轉(zhuǎn)義碼太長,是否可以折行呢:

在階段1中,由于\u后面的格式不符合HHHH,不會做相關(guān)字符處理,而在階段2中將兩行連接起來后,形成了\u1234這樣一個(gè)合法的通用字符名,那么編譯器會回到階段1將其繼續(xù)按通用字符名來識別嗎?

測試的話,gcc和clang是會識別的,但這個(gè)問題的標(biāo)準(zhǔn)答案其實(shí)是未定義行為,所以請不要這樣寫代碼


【C/C++】在預(yù)處理之前,編譯器做了什么事情?- 翻譯階段1&2的評論 (共 條)

分享到微博請遵守國家法律
米易县| 包头市| 定南县| 潮州市| 葵青区| 辽阳县| 乐平市| 上饶县| 莱芜市| 南昌市| 阳春市| 焦作市| 福州市| 乐平市| 竹溪县| 肥西县| 霍林郭勒市| 呈贡县| 建湖县| 特克斯县| 姚安县| 九寨沟县| 大埔县| 恩施市| 城固县| 泾源县| 徐水县| 增城市| 奉化市| 始兴县| 云林县| 英山县| 通许县| 白山市| 宁南县| 准格尔旗| 丰顺县| 遂昌县| 名山县| 东城区| 孝感市|