C陷阱與缺陷整理一

來源:互聯網
上載者:User

標籤:c   陷阱   缺陷   

1.詞法分析中的“貪進法”
    C語言的某些符號,例如/、*和=,只有一個字元長,稱為單字元符號。而C語言中的其他符號,例如/*和==,以及標識符等都包含了多個字元,稱為多字元符號。當C編譯器讀入一個字元‘/‘後又跟了一個字元‘*‘,那麼編譯器就必須做出判斷:是將其作為兩個分別的符號對待,還是合起來作為一個符號來對待。C語言對這個問題的解決方案可以歸納為一個很簡單的規則:每一個符號應該包含儘可能多的字元。也就是說,編譯器將程式分解成符號的方法是,從左至右一個字元一個字元地讀入,如果該字元可能組成一個符號,那麼再讀入下一個字元,判斷已經讀入的兩個字元組成的字串是否可能是一個符號的組成部分;如果可能,繼續讀入下一個字元,重複上述判斷,直到讀入的字元組成的字串已不再可能組成一個有意義的符號。
    但是需要注意的是,除了字串與字元常量,符號的中間不能嵌有空白(空格符、定位字元和分行符號)。注意下面兩個例子:
a---b等價於a -- - b
a---b不等價於a - -- b
y = x/*p 不能實現預期的目的,可以修改為y = x/(*p)或y = x/ *p

2.用雙引號引起的字串,代表的是一個指向無名數組起始字元的指標,該數組被雙引號之間的字元以及一個額外的二進位值為0的字元‘\0‘初始化。

3.‘yes‘這種寫法的字元,根據編譯器的不同會得到不同的處理:大多數編譯器會理解為一個整數值,由‘y‘、‘e‘、‘s‘所代表的整數值按照特定編譯器實現中定義的方式組合得到;Borland C++v5.5和LCC v3.6採取的做法是忽略多餘的字元,最後的整數值即第一個字元的整數值;在Visual C++6.0和GCC v2.95中採取的做法是,依次用後一個字元覆蓋前一個字元,最後得到的整數值即最後一個字元的整數值。

4.任何C變數的聲明都由兩部分組成:類型以及一組類似運算式的聲明符。聲明符從表面上看與運算式有些類似,對它求值應該返回一個聲明中給定的類型的結果。
float ff();
這個聲明的含義:運算式ff()求值的結果是一個浮點數,也就是說,ff是一個返回值為浮點類型的函數。
float *pf;
這個聲明的含義:*pf是一個浮點數,也就是說pf是指向一個浮點數的指標。
float *g();
這個聲明的含義:*g()是一個浮點數,g()等價於上面的pf一樣理解,g是一個函數,函數的值就是函數的返回值,所以g()返回的值應該是上例中的pf一致的類型,也就是一個浮點型的指標。
float (*h)();
這個聲明的含義:(*h)()是一個浮點數,然而(*h)()是一個函數,所以h就是一個函數指標,h指向的這個函數返回一個浮點型的數。

5.強制類型轉換符的構建
從聲明變數出發,去掉聲明中的變數名和聲明末尾的分號,然後將剩餘的部分用一個括弧括起來即可。
float (*h)();-->(float (*)())
後面的強制類型轉換符的含義:返回值為浮點類型的函數的指標。
現在來分析(*(void(*)())0)()的含義
很明顯在常數0之前是一個強制類型轉換符:(void(*)()),這個強制類型轉換符的含義是:返回值類型為void類型的函數指標。所以這整個運算式的含義就是,將0轉換為一個返回值類型為void類型的函數指標,然後通過*運算子取得這個0所指向的函數,然後調用它。使用typedef來實現這個功能更為清晰:
typedef void (*funcptr)();
(*(funcptr)0)();
複雜一點的一個例子:
void (*signal(int, void(*)(int)))(int);
含義:首先把signal(int, void(*)(int))看做一個整體,這樣的話這個整體應該代表的是一個函數指標,外層通過*運算子調用這個指標指向的函數。然而signal(int, void(*)(int))本身就比較好理解了,signal是一個函數,接收的參數第一個為int類型的值,另一個參數為一個函數指標。然而既然這個整體表示一個函數指標,前面也提到過函數本身代表一個值的話其實也就是說該函數的返回值是一個什麼樣的值,所以signal函數返回一個函數指標,該指標類型為void (*)(int)。那麼void (*signal(int, void(*)(int)))(int)聲明的signal如何調用呢?其實用法很簡單:
signal(int m, void(*ptr)(int);就可以實現調用,上面這個聲明方式的用處在於使得這個函數的返回值變得更加有意義,調用者可以定義一個void (*)(int)類型的指標來接收signal函數的返回值。
這個功能同樣可以由typedef來實現,同樣會變得更為清晰:
typedef void (*HANDLER)(int);
HANDLER signal(int, HANDLER);

6.當需要為一個變數添加符號的時候可以採用下面的這種寫法:
int a = 3;
a = -a;

7.C語言的初始化例表中允許多書寫一個逗號,這有什麼作用?
分析:這是為了詞法分析器的方便,這樣書寫之後,每一個用來初始化的變數都是以逗號結束,編譯器處理起來更為方便。

8.定義一個數組a之後,a除了作為運算子sizeof的參數之外,其他的所有情形中a都代表的是a中下標為0的元素的指標,這個在多維陣列的情況下仍然適用,因為在C語言中多維陣列是通過一維數組類比出來,只不過一維數組的每個元素都可以是其他的各種類型的變數,當然也可以是另一個數組。所以如下的例子:
int a[3];
*a = 2;
*(a+1) = 3;
分別操作的是a數組中的第0個元素和第1個元素,但是這樣操作起來往往比較麻煩,所以現在常用的寫法a[0]、a[1]、a[2]是上面的寫法的簡寫形式。*(a+1)等價於*(1+a),而*(a+1)的簡寫形式為a[1],而*(1+a)的簡寫形式為1[a],所以a[1]等價於1[a]寫法,很多編譯器都不會報錯,但是不推薦第二種寫法!!

9.下面的例子輸出會是多少?
int a[2][3];
printf("%d, %d", sizeof(a+1), sizeof(a[1]));
分析:先不說這個輸出結果為多少,首先來一步步分析需要輸出的內容,sizeof(a+1)編譯器在碰到這句話的時候,會把a當做一個指標來處理,因為數組+1沒有意義,只有數組的指標+1才有意義,所以這裡測試出來的仍然只是一個指標的大小,那麼如果是32位的PC的話輸出就會是4。再來分析sizeof(a[1]),這個測的是一維數組a的第2個單元的大小,然而第二個單元裡面存放的是什麼呢?第二個單元裡面存放的是一個3個單元的數組,所以這個運算式測試出來的值為12。
答案:4, 12

10.malloc()函數分配記憶體失敗的時候會返回一個null 指標,在使用malloc的時候一定要判斷返回的值是否為空白指標,否則會產生意想不到的後果。

C陷阱與缺陷整理一

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.