從較低的層面考察,程式是由符號(token)序列組成的,將程式分解成符號的過程,稱為詞法分析。
這節主要分析在程式被詞法分析器分解成各個符號的過程中可能出現的問題。
編譯器中負責將程式分解成乙個乙個符號的部分,一般稱之為詞法分析器。
c語言中符號之間的空白(包括空格符,製表符和換行符)將被忽略。
術語符號(token)指的是程式的乙個基本組成單元, 其作用相當於句子中的單詞。
(乙個單詞無論出現在什麼句子,他所代表的意思大多數情況下不會有變化,是乙個基本的表義單元)
以此類推,符號就是程式中的乙個表義的基本單元。而組成符號的字串行就不同,同一組字串行在某個上下文環境中屬於乙個符號,而在另乙個上下文環境中,可能屬於完全不同的乙個符號。
程式中單個字元孤立來看並沒有什麼意義,只有結合上下文才能體現出
//eg 1:
p->s=
"->"
;上面語句兩處出現了'-'字元,分別出現在等號左右,意義大相徑庭。
上述兩個字元分別是不同符號的組成部分。
等號左邊的「-」代表"->"符號的組成部分,
等號右邊的「-」是字串「-
>」的組成部分。
符號「-
>」的含義也是組成該符號的字元"->"所沒有的。
pascal和ada:
使用:=作為賦值運算子,符號==作為比較運算子;
c語言:
使用=作為賦值運算,符號 ==作為比較;
(一般而言,賦值運算子相對於比較運算子使用的更加頻繁,因此字元數較少的=就被賦予了更常用的含義)
在c語言中賦值符號被作為一種操作符對待,故此重複進行賦值操作(a=b=c)可以很容易的書寫,並且賦值操作還可以被嵌入到更長的表示式中。
潛在問題:本意是做比較時,誤寫成了賦值操作
//eg:1
if(x = y)
break
;//該語句的本意似乎是要檢查x和y是否相等
//在這裡誤寫成了賦值操作,實際上是將y賦值給了x,然後檢查該值是否等於0。
//eg:2
while
(c=' '
|| c ==
'\t'
|| c ==
'\n');
c=getc
(f);
//該程式的本意是跳過檔案中的空格,製表符,換行符
//由於在比較c和字元空格時,誤將比較運算子寫成了賦值運算子
//由於賦值運算子=的優先順序低於邏輯運算子||,其實是將下表示式賦值給了cc=(
' '|| c ==
'\t'
|| c ==
'\n');
//因為空格不等於0,空格的ascii碼為32,無論c此前為何值,上述表示式求值的結果都是1,因此檔案將一直迴圈
//檔案迴圈結束,如果getc庫函式在檔案指標到達檔案結尾後是否允許繼續讀取字元,如果允許,迴圈一直進行,成為死迴圈
//eg:3
//某些c的編譯器發現型如r1=r2的表示式出現在迴圈語句的條件判斷語部分,會給出警告資訊提示。
//當確實需要對變數進行賦值並檢查該變數的新值是否為0,為了避免來自類編譯器警告,我們不應該關閉,而應該使用顯示比較
if(x = y)
foo();
//應該寫作if(
(x = y)!=0
)foo()
;//eg:4if(
(filedesc ==
open
(ar**[i],0
))<0)
error()
;//open 如果執行成功將返回0或者正數,如果執行失敗將返回-1
//本意是將open函式的返回值放到變數filedesc,然後通過比較filedesc和0來檢查open函式是否執行成功
//實際則是比較open的返回值和filedesc,然後檢查比較的結果是否小於0,
//由於比較運算子的結果只可能是0/1,不會小於0,所以error函式將沒有機會被呼叫
//如果**被執行,似乎一切正常,除了變數的值不是open的返回值
c語言的某些符號,例如「/」,「*」和「=」只有乙個位元組長,稱為單字元符號。
而其他符號例如「/ *」和「==」以及識別符號,包括了多個字元,稱為多字元符號。
當c編譯器讀入乙個字元「/」後又跟了乙個字元「*」,那麼編譯器就必須做出判斷:將其作為兩個分別的符號對待還是合起來作為乙個符號對待。
c語言的解決方案:每乙個符號應該包含盡可能多的字元。
編譯器將程式分解程式分解成符號的方法是:從左到右乙個乙個字元的讀入,如果該字元可能組成乙個符號,那麼再讀入下乙個字元,判斷已經讀入的兩個字元組成的字串是否可能是乙個符號的組成部分;如果可能繼續讀入下乙個字元,,重複上述判斷,直到讀入的字元組成的字串不在可能組成乙個有意義的字元。貪心法:如果編譯器的輸入流截止至某個字元之前都已經被分解成乙個個符號,那麼下乙個符號將包括從該字元之後可能組成乙個符號的最長字串
除了字串與字元常量,符號的中間不能嵌有空白(空格/製表/換行符)。 == 是單個符號,= =是兩個符號
a--
-b;//從左到右讀入 先識別出最長自減為-- 剩下為減- 等同於 第二條語句
a --
-b;a -
--b;
//空格原因 先識別出減- 再識別出 --自減操作
// 如果/為判斷下乙個符號而讀入的第乙個字元,而且/後緊接著*,
//那麼無論上下文如何,這兩個字元都將被當作乙個符號/*,表示一段注釋的開始
//eg:1
y = x/
*p;//該語句的本意似乎是用x除以p指向的指,再把結果賦值給y
//實際上/*被編譯器理解為一段注釋的開始,編譯器將不斷地讀入字元,直到*/出現為止
y =x;
//該語句直接將x的值賦值給y,不考慮後面的字元 等同於本語句
//將上面語句重寫.得到語義注釋表達的原意
y = x /
*p;y = x /
(*p)
;//老版本編譯器的樂趣(滑稽臉)
//允許=+類代表+=類
//eg:2a=-
1;//從左到右讀入 可得
a =-1;
//等號和減號結合
a = a-1;
//如果原意是給a賦值,那麼該語句完全偏離了原意。
a=/*b; //編譯器還會理解為注釋碼?不會的。。。滑稽臉
a =/ *b; //滿足從左到右讀入,a = a/ *b
前面加0,該常量將被視為8進製
前面加0x,該常量將被視為16進製制
c語言中,單引號和雙引號含義不同。
單引號引用的乙個字元,實際上代表乙個整數,值對應於該字元再編譯器採用的字符集中的序列值。
雙引號引用的字串,代表了指向無名陣列起始字元的指標,陣列被雙引號中的字元和二進位製字\0』初始化
//單引號代表數值,雙引號代表指標
char arr1=
;char arr2=
;//上述兩者等效
整形數的儲存空間(16/32)一般可以容納多個字元,有的編譯器允許在乙個字元常量/字串常量中包括多個字元。『yes』代替「yes」不會被編譯器檢測到,後者的定義是指向無名陣列的首位址的指標,前者的含義不明。大多數編譯器理解為:乙個整數值,由字元代表的整數值按照特定編譯器實現中定義的方式組合得到。 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 陷阱與缺陷 (一)詞法陷阱
碼字不易,對你有幫助點讚 關注支援一下作者 看更多乾貨,獲取第一時間更新 練習上傳至 0.不同於 當程式設計師本意是作比較運算時,卻可能無意中誤寫成了賦值運算。1.本意是檢查 x 與 y 是否相等 if x y break 實際上是將 y 的值賦值給了 x 然後再檢查該值是否為 0 2.本意是跳過檔...
c陷阱與缺陷 陷阱
例1 if x y break 這就話的意思就是把y賦值x,判斷x是否為0,實則是在判斷y是否為0 例2 while c c t c n 這句話的意思就是 c t c n 賦值給c,而有 符本身就是不為0的數,所以這就是while 1 的意思。例3 int x 4,p new int p 2 cou...