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

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

【C/C++】給編譯器攪局的詞法 - 原始字符串

2023-04-04 19:07 作者:冒-_-泡  | 我要投稿

前文提到,翻譯階段3是面向預(yù)處理的詞法分析,雖然和正式編譯的詞法分析,或者說在編譯原理中提到的有一些區(qū)別,但也大差不差,基本目的就是將字符序列分割成一個個詞(也稱token),當(dāng)然光確定具體的分割點還不夠,重要的是確定每個詞的含義和內(nèi)容

在cpprefrence的階段3描述中,有這么一條:

撤回在任何原始字符串字面量的首尾雙引號之間在階段 1 和 2 期間進(jìn)行的所有變換。

(C++11 起)

原始字符串是C++11新增的語法(或者確切說是詞法),不考慮其他前綴的話,格式是:

和Python的原始字符串類似,兩邊的括號和d字符序列是用來指定開始結(jié)束的,通過自定義特別的開始結(jié)束標(biāo)記,在中間就可以安全使用雙引號之類字符了,原則上說,原始字符串表示的是代碼中原始的內(nèi)容,支持跨行,如上面的例子,表示“\”、“n”和換行這三個字符,具體細(xì)節(jié)語法書有詳細(xì)說明,就不細(xì)說了

一般來說,編譯流程是一步步前進(jìn)的,即便有回退,也是局部性的(比如語法分析中的一些匹配嘗試),那為什么階段3會有上面這條撤回的奇怪規(guī)定呢,舉個例子說明:

對于這個代碼中的原始字符串,根據(jù)前面對翻譯階段1和2的解釋,我們知道編譯器會在階段1進(jìn)行字符替換,三標(biāo)符“??/”被替換為反斜杠(注:為舉例方便,下面都默認(rèn)開啟三標(biāo)符語法),而到了階段2,會進(jìn)行行尾反斜杠的行拼接處理,這樣這個字符串就被改成空串了。類似的情況也會出現(xiàn)在階段1需要處理的\u和\U通用字符轉(zhuǎn)義的處理中

但是對一般的程序員來說,寫出這種代碼是為了表示什么?從標(biāo)準(zhǔn)角度,我們知道三標(biāo)符和通用符的處理隸屬于階段1,而并不是\r、\n、\ooo、\xHH之類的字符轉(zhuǎn)義,后者從概念上是隸屬于字符和字符串字面量,在階段5處理。但是,這種認(rèn)知對于一般人來說過于復(fù)雜,而且過于接近底層實現(xiàn)了,更何況三標(biāo)符這種屬于歷史原因的遺留

所以對于原始字符串來說,C++11將其設(shè)計為內(nèi)容和代碼中完全一致(注:原則上,下面有特例說明),無視階段1和2的處理,這在理解上會簡單很多,而且能規(guī)避一些麻煩的情形,比方說,如果階段1和2的處理被接納進(jìn)來,用原始字符串表示原始的"??="這三個字符就會很麻煩,至少你不能寫R"x(?\?=)x",因為就算繞過了階段1,“\?”的轉(zhuǎn)義在這里也是無效的,憑空多了一個反斜杠了

于是,由于階段3是詞法處理,需要識別出字符串這一類token,標(biāo)準(zhǔn)就在這里要求對原始字符串撤回階段1和2的任何改動了,將內(nèi)容改回代碼中的樣式

為什么不能在一開始就規(guī)避,例如規(guī)定在階段1中,對原始字符串中的三標(biāo)符和通用符不做處理呢?答:因為沒法識別,字符串token必須在詞法階段才可以識別,階段1和2只是面向字符序列的簡單文本處理

這個規(guī)定給編譯器實現(xiàn)帶來了一個不大不小的麻煩,這個需求說大也算不上大,但是實際處理起來又挺繁瑣的,比方說,我們可以記錄下階段1和2所有替換的位置和原始內(nèi)容,從而方便實現(xiàn)回滾,但如果只記錄偏移位置,在回滾替換時又得注意偏移之間的影響等等

不過根據(jù)之前視頻講過的AS-IF原則,如果用戶不關(guān)心階段1和2的處理結(jié)果(一般需要關(guān)注的最早的代碼中間結(jié)果是預(yù)處理之后,即階段4的),那么編譯器也可以將階段123合并成一個流程來處理,即一邊進(jìn)行字符和行處理,一邊進(jìn)行詞法分析,如此在后者解析到原始字符串內(nèi)容時,skip掉前者的邏輯就行了

根據(jù)以上的論述,我們是不是就可以認(rèn)為:原始字符串表示的是代碼中原始的內(nèi)容?其實我上面還加了個前提:“原則上說”,看這個例子:

注意,這里采用了vim中的表示法,“^M”在這里表示“\r”這個字符,而不是“^”和“M”

我們知道不同平臺的換行是有些不同的,微軟的系統(tǒng)用“\r\n”,而POSIX系統(tǒng)(如*nix、Mac)是“\n”,一般而言,對于C和C++,這個差別不會有什么影響,因為代碼是換行和空白符不敏感的,除了單行注釋和預(yù)處理命令等特殊語法外,你把所有代碼寫到一行都沒有關(guān)系,在原始字符串出現(xiàn)之前,行末有沒有\(zhòng)r關(guān)系不大,雖然\r并非C的基礎(chǔ)字符集,編譯器還是將其視為一個空白符(例如當(dāng)做空格或換行)

但是在原始字符串中,如果出現(xiàn)了換行是\r\n,或\r單獨出現(xiàn)的情形,那情況就要復(fù)雜了,由于\r不是C代碼的基礎(chǔ)字符集,所以在代碼文件被編譯器讀入后,轉(zhuǎn)換為什么是一個實現(xiàn)定義行為,例如以上代碼,在我這邊的linux環(huán)境下,用gcc和clang測試:

看上去這倆編譯器對于行尾的\r\n都轉(zhuǎn)成了\n,而前面這個單獨出現(xiàn)的\r,gcc將其轉(zhuǎn)為\n,clang則保留為\r不變

如果說這種差別不是專門針對\r的,而是上述的“非基礎(chǔ)字符集字符的轉(zhuǎn)換按實現(xiàn)定義”導(dǎo)致,那問題就嚴(yán)重一點,因為除了基礎(chǔ)字符集外,還有“@”、“$”和碼值126以后的字節(jié)都在內(nèi),如果各編譯器處理有差異,那字符串里面寫漢字會不會有亂碼的可能?

幸運的是現(xiàn)在還沒發(fā)現(xiàn)在中文編碼方面的問題,而且C++本來也有unicode字面量,測了一些case,好像就\r這里是有差異,具體規(guī)則我也沒找到權(quán)威說明,只能說如果需要對字符串中每個字節(jié)有把控的需求,那使用原始字符串還是慎重一些,用普通字符串轉(zhuǎn)義處理較為穩(wěn)妥了

至于gcc將\r轉(zhuǎn)為\n的行為是否違反了上面這條撤回的規(guī)則,可以這樣理解:撤回處理是指將原始字符串內(nèi)容部分撤回至翻譯階段開始的狀態(tài),而此時是出于將源代碼從磁盤讀入內(nèi)存,且將字節(jié)序列轉(zhuǎn)換為字符序列之后,即關(guān)于\r的處理并非階段1,而是最早的文件讀取階段,這樣就容易理解了,畢竟如果用fopen的文本模式打開文件的話,行末\r\n讀出來就只是\n了,編譯器此時甚至都感知不到\r的存在

【C/C++】給編譯器攪局的詞法 - 原始字符串的評論 (共 條)

分享到微博請遵守國家法律
叶城县| 临武县| 平舆县| 肇源县| 江安县| 海宁市| 广州市| 丹凤县| 铁岭市| 息烽县| 宣威市| 永靖县| 彭阳县| 深泽县| 承德县| 曲麻莱县| 泰顺县| 自治县| 蓬溪县| 嘉义县| 信宜市| 惠东县| 两当县| 鹤壁市| 新宾| 湖南省| 习水县| 吉木萨尔县| 高青县| 九龙县| 桃源县| 吐鲁番市| 松原市| 蒙城县| 庆安县| 若尔盖县| 连城县| 陇南市| 四川省| 宝兴县| 太白县|