C 陷阱與缺陷 (一)詞法陷阱

2021-10-06 20:28:50 字數 4047 閱讀 6476

碼字不易,對你有幫助點讚/**/關注支援一下作者

看更多乾貨,獲取第一時間更新

**,練習上傳至:

0.=不同於==

當程式設計師本意是作比較運算時,卻可能無意中誤寫成了賦值運算。

1.本意是檢查 x 與 y 是否相等:

if

(x = y)

break

;

實際上是將 y 的值賦值給了 x ,然後再檢查該值是否為 0 。

2.本意是跳過檔案中的空白字元:

while

(c =

''|| c ==

'\t'

|| c ==

'\n'

) c =

getc

(f);

因為' '不等於 0 (' '的 ascii 碼值為 32),那麼無論變數為何值,上述表示式求值的結果都為 1,因此迴圈將進行下去直到整個檔案結束。

c 編譯器發現形如 x = y 的表示式出現在選擇語句,迴圈語句的條件判斷部分時,會給出警告。當確實需要對變數進行賦值時,為了避免警告,我們應該這樣處理:

if

((x = y)!=0

)foo()

;

如果將賦值寫成了比較,也會造成混淆:

if

((filedesc ==

open

(ar**[i],0

))<0)

error()

;

本例中,open 執行成功返回非零值,失敗返回 -1。本意是將 open 函式的返回值儲存在變數 filedesc 中,然後將其和 0 比較大小,判斷 open 執行是否成功 。==運算子的結果只可能是 1 或 0,永遠不會小於 0,所以 error() 將沒有機會被呼叫。

1.&|不同於&&||

比較i & ji && j,只要 i 和 j 是 0 或 1 ,兩個表示式的值是一樣的(|||同理。)。然而,一旦 i 和 j 的值為其他,兩個表示式的值不會始終一致。

另乙個區別是運算元帶有自增自減的運算:

i & j++, j 始終會自增;但是i && j++有時 j 不會自增。

2. 詞法分析中的「貪心法」

當 c 的編譯器讀入乙個字元/後跟著乙個字元*時,那麼編譯器就必須做出判斷:時將其作為兩個符號對待,還是合起來作為乙個符號對待。這類問題的規則:每個符號應該包含盡可能多的符號

例如:a---b(a--) - b含義相同,而與a - (--b)含義不同。

又如:下面的語句本意是 x 除以 p 指向的值然後將結果賦值給 y

y = x/*p;

但是,實際上/*被編譯器理解為一段注釋的開始。

將上面的語句重寫如下:

y = x /

*p;

或者:

y = x/

(*p)

;

老版本的編譯器允許使用=+來代表現在+=的含義,這種編譯器會將:

a=-1

;

理解為:

a =-1

;

即為:

a = a -

1;

因此,如果程式設計師的原意為:

a =-1

;

那麼結果會讓其大吃一驚。

再如:

a=

/*b;

在老版本的編譯器會將其當作:

a =

/*b;

3. 整型常量

許多編譯器會把 8 和 9 作為把八進位制的數字處理,這種處理方式**於八進位制數的定義。例如:0195 的含義是1x8^2 + 9x8 + 5x8^0也就是 141(十進位制)或 0215(八進位制)。ansi c 標準中禁止這種用法。

4. 字元與字串

單引號引起的乙個字元實際上代表乙個整數。整數值對應於該字元在編譯器採用的字符集中的序列值。因此,對於採用 ascii 字符集的編譯器而言,'a'的含義與 97 (十進位制)嚴格一致。

用雙引號引起的字串,代表的確實乙個指向無名陣列起始字元的指標。該陣列被雙引號之間的字元以及乙個額外的二進位制值為 0 的字元\0初始化。

比如,下面的這個語句:

printf

("hello world\n"

);

等價於:

char hello=

;printf

(hello)

;

整數型(一般為 16 或 32 位)的儲存空間可以容納多個字元(一般為 8 位),因此有的編譯器允許在乙個字元常量(以及字串常量)中包含多個字元。也就是說:用'yes'代替"yes"不會被該編譯器檢測到。前者的含義大多數編譯器理解為乙個整數值,由'y','e','s'所代表的整數值按照特定編譯器實現中的定義方式組合得到。

練習 1

某些 c 編譯器允許巢狀注釋。請寫乙個測試程式,要求:無論編譯器是否允許巢狀注釋,該程式都能正常通過編譯,但是兩種情況下程式執行結果不同。

對於符號串行:

/*/**/

"*/"

如果允許巢狀注釋,上面的符號串行表示:乙個單獨的雙引號",因為最後的注釋符前出現的符號都會被當作注釋的一部分。

如果不允許巢狀注釋,上面的符號就表示乙個字串:"*/"

doug mcllroy 發現了下面這個令人拍案叫絕的解法:

/*/*/0*

/**/

1

這個解法主要利用了編譯器作詞發分析時的「貪心法」規則。

如果編譯器允許巢狀注釋,則將上式解釋為:

/* /*/0*

/**/

1

上式的值為 1

如果編譯器不允許巢狀注釋,則解釋為:

/* / */0*

/**/

1

也就是0*1,值為 0

練習 2

a+++++b的含義是什麼?

上式唯一有意義的解析方式就是:

a++

+++b

可是,根據「貪心法」的規則,上式應該被解釋為:

a++

+++ b

等價於:

(a++)++

+ b;

但是a++的值不能作為左值,因此編譯器不會接受 a++ 作為後面 ++ 運算的運算元。

以上就是本次的內容,感謝**。

最後,關注我,看更多乾貨!

我是程式圓,我們下次再見。

C陷阱與缺陷(一)詞法 陷阱

第一章 詞法 陷阱 1 不同於 1 while c c t c n 賦值運算子 的優先順序低於邏輯運算子 相當於 c t c n 賦給了c,條件永遠為真 2 if x y 應該寫成if x y 0 對變數進行賦值並檢查該變數的新值是否為0 2 和 不同於 和 按位運算子 和 邏輯運算子 和 3 詞法...

C缺陷與陷阱 詞法陷阱

從較低的層面考察,程式是由符號 token 序列組成的,將程式分解成符號的過程,稱為詞法分析。這節主要分析在程式被詞法分析器分解成各個符號的過程中可能出現的問題。編譯器中負責將程式分解成乙個乙個符號的部分,一般稱之為詞法分析器。c語言中符號之間的空白 包括空格符,製表符和換行符 將被忽略。術語符號 ...

C陷阱與缺陷第一章 詞法「陷阱」

1.不同於 為賦值運算,為比較 2.和 不同於 和 按位運算子 邏輯運算子 3.詞法分析中的 貪心法 c語言的某些符號,例如 只有乙個字元長,還有其他符號,例如 有多個字元。編譯器1將程式分解成符號的方法是,從左到右乙個字元乙個字元地讀入,如果判斷已經讀入的兩個字元組成的字串是否可能是乙個符號的組成...