碼字不易,對你有幫助 點贊/轉發/關注 支持一下作者
微信搜公衆號:不會編程的程序圓
看更多幹貨,獲取第一時間更新
代碼,練習上傳至:https://github.com/hairrrrr/C-CrashCourse
一 內容
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(argv[i], 0)) < 0)
error();
本例中,open 執行成功返回非零值,失敗返回 -1。本意是將 open 函數的返回值存儲在變量 filedesc 中,然後將其和 0 比較大小,判斷 open 執行是否成功 。==
運算符的結果只可能是 1 或 0,永遠不會小於 0,所以 error() 將沒有機會被調用。
1. &
和|
不同於&&
和||
比較 i & j
和 i && 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[] = {'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '\n', 0};
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 缺陷與陷阱》
以上就是本次的內容,感謝觀看。
如果文章有錯誤歡迎指正和補充,感謝!
最後,如果你還有什麼問題或者想知道到的,可以在評論區告訴我呦,我在後面的文章可以加上。
最後,關注我,看更多幹貨!
我是程序圓,我們下次再見。