程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> 淺談C語言中存在的陷阱和缺陷

淺談C語言中存在的陷阱和缺陷

編輯:C++入門知識

下面介紹C語言的陷阱和缺陷:

詞法陷阱:

1、 = 不同於==不要在程序中將兩者寫錯,小心。將表達式與常量比較時,可將常量放在左邊。

2 、&和| 不同於&& 和 ||.

3、 詞法分析中的貪心法:每個符號應該包含盡可能多的字符。如果編譯器的)輸入流截至某個字符前都已經分解為一個個符號,那麼下一個符號將包括從該字符之後可能組成一個字符的最長字符串。如y = x/*p,那麼/*將作為一個符號對待。

4、 如果一個整形變量第一個字符是0,那麼該常量被視為8進制數。

5 、Char c = ‘cxf’。在vc和Gcc中,依次用後一個字符覆蓋前一個字符,最後得到的整數值是最後一個字符的整數值。

語法陷阱:

1 、c變量聲明由類型和一組類似表達式的聲明符組成。聲明符與表達式類似,對他求值返回一個聲明中給定類型的結果。如float f, f))。

知道了如何聲明一個變量,那麼該類型的類型轉換符就很容易得到了:將聲明中的變量名和分號去掉,再將剩余的部分用個括號“封裝”起來即可。如float*h) ),則float*))就是“指向返回值是浮點類型的函數的指針”的類型轉換符。*void*)))0))調用地址為0位置的的例程。

2 、運算符優先級:單目運算符,算術運算符,移位,關系,邏輯,條件, 賦值。

3 、switch語句中case中,不要忘記break,若刻意要省略,請加注釋。

4 、C語言中只有一維數組,而且數組的大小必須在編譯期間就作為一個常數確定下來。多維數組是通過一維數組仿真的,因為數組的元素可以是任何對象,當然也可以是數組。

對數組,我們只能做兩件事,確定其大小,以及獲得指向該數組下標為0的元素的指針。其它的有關數組的操作,實際上是通過指針進行的。

語義陷阱:

1 、空指針並不等於空字符串。編譯器保證由0轉換而來的指針不等於任何有效的指針。當將0賦值非一個指針變量時,絕對不能企圖使用該指針指向的內存中存儲的內容。

2 、在使用范圍時,使用不對稱邊界方式。第一個是“入界點”序列中第一個被占用的元素),第二個是“出界點”序列中第一個被釋放的元算)。Forint I = 0 ; I < 10; i++)。盡量不要使用Forint I = 0 ; I <=9; i++)。

3 、數組的下標如果用入界口加出界口來表達即10個元素,其下標為0 <= n < 10 ),則元素個數即為上界與下界之差,即下界。若為空,則上界等於下界。任何情況下上界也永遠不可能小於下界。

盡量采用非對稱邊界法。

一個有N個元素的數組 ,我們可以使用a[N]進行比較和賦值,但不能引用其內容。

4 、C語言中只有4個運算符存在規定的求值順序:&&,| |, ?:和,。其他的運算符對器操作數求值的順序是未定義的。特別的是,賦值運算符並不保證任何求值順序。Y[i]=X[i++] 錯誤。

5 、記得為main提供返回值。

連接:

1 、為避免命名沖突,請對變量或函數使用static修飾符。為了定義與庫函數中同名的函數,可將文件中要定義的函數加static修飾。

2 、使用外部函數前,一定要聲明。否則,沒有聲明,函數返回值將默認為整型。

3 、外部聲明要與定義類型一致。不能聲明是extern int n,而定義是long n.

4 、同一個外部變量在不同的地方被聲明為不同的類型,這種錯誤大部分編譯器是檢不出來的。

  1. char file[]= "/etc/password"; 

  1. extern char* file; 

是不一樣的。

庫函數:

1 、注意getchar)返回整型,不是字符型。

2 、為了保持與過去不能同時進行讀寫操作的程序的向下兼容性,一個輸入操作不能隨後直接緊跟一個輸出操作,反之依然,如果要同時進行輸入和輸出操作,必須在其中插入fseek函數的調用。例:

  1. FILE *fp;  
  2. struct record rec;  
  3. while (fread((char *)&rec, sizeof(rec),1,fp) = 1)  
  4. {  
  5. if(/* */)  
  6. {  
  7. fseek(fp, -(long)sizeof(rec), 1);  
  8. fwrite((char *)&rec, sizeof(rec), 1,fp);  
  9. fseek(fp, 0l,1);  
  10. }  

3、 緩沖輸出和內存分配:

可通過setbuf函數控制程序的緩沖輸出。

  1. #include <stdio.h>  
  2. void main(void)  
  3. {  
  4. int c;  
  5. char buf[BUFSIZ];  
  6. setbuf(stdout,buf);  
  7.  
  8. while((c = getchar()) != EOF)  
  9. putchar(c);  

這個是不對的。buf最後一次被清空是在什麼時候?答案是在main函數結束之後,作為程序交回控制給操作系統之前C運行時庫所必須進行的清理工作的一部分。但是在此之前buf已經被釋放。

解決方法一是加上static 聲明。也可以把buf聲明完全移到main函數之外。第二種辦法是動態分配緩沖區,在程序中並不主動釋放分配的緩沖區

4 、不能直接使用errno檢測錯誤,應先檢測作為錯誤指示的返回值,確定程序已經執行失敗。然後,再檢查errno,搞清原因。

/* 調用庫函數 */

if返回的錯誤值)

檢查errno

5 、庫函數signal

從理論上說,一個信號可能在C程序執行期間的任何時刻上發生,甚至可能出現在某些復雜的庫函數如malloc)的執行過程中。

因此從安全的角度講,信號的處理函數不應該調用上述類型的庫函數。基於同樣的原因,從signal處理函數中使用longjump退出,通常情況下也是不安全的:因為信號可能發生在malloc 或者其它庫函數開始更新某個數據結構,卻又沒有最後完成的過程中。因此signal處理函數能夠做的安全的事情,似乎就只有設置一個標志然後返回,期待以後主程序能夠檢查到這個標志,發現一個信號已經發生。

然而,就算這樣做也並不總是安全的。當一個算術運算錯誤引發一個信號時,某些機器在signal處理函數返回後還將重新執行失敗的操作。因此對於算術運算錯誤,signal處理函數的惟一安全、可移植的操作就是打印一條出錯消息,然後使用longjump或exit立即退出程序。

當一個程序異常終止時,程序輸出的最後幾行常常會丟失,原因是緩沖。

預處理器:

1、 不要忽視宏中的括號。

2、 宏不是函數。將宏中的參數都加上括號,將整個結果表達式也括起來。防止副作用。

3、 宏不是語句:#define asserte) void)e)||_assert_error_FILE_,_LINE_)))

4 、宏不是類型定義;不要用#define定義類型,而是用typedef定義新類型。

可移植性:

1 、因為字符串常量可以用來表示一個字符數組,所以在數組名出現的地方都可以用字符串常量末端替換。 如: "0123456789"[n%10]

2、 注意C標准的變化,新特性的使用。

3 、c標准所能保證的只是,c實現必須能夠區別出前6個字符不同的外部名稱,且並沒有要求區分大小寫。避免諸如:print_char),print_int)等

4 、整數長度的相對長度規定:short類型的值肯定能被int型容納,int型肯定能被long型整數容納;一個普通int類型)整數足夠大以容納任何數組下標;字符長度由硬件特性決定。

5 、隨機數最大值:RAND_MAX

希望通過以上內容的介紹,能給你帶來一定的幫助,幫助你認清一些小知識的真相。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved