1. 程式人生 > >C語言陷阱一

C語言陷阱一

1、

while ( c= ' ' || c== '\t' || c== '\n')
	c=getc(f);

程式設計師有時候會將=寫成==,因為賦值運算子=的優先順序要低於邏輯運算子||,因此實際上上述表示式是把 **’ ’ || c== ‘\t’ || c== ‘\n’**的值賦值給c,因為’ '不等於0(ASCII碼值為32),所以無論變數c為何值,上述表示式的結果都是1,因此迴圈將一直下去知道檔案結束。

2、

if((filedesc == open (argv[i], 0))<0)
		error();

這類錯誤是把賦值運算子誤寫成比較運算子的型別,open函式若執行成功,則返回0或正數,若open函式執行失敗,將返回-1。因為比較運算子==的結果只能是0或1,所以函式error()將沒有機會被呼叫。若程式碼執行,似乎一切正常,除了變數filedsec的值不再是open函式的返回值,在某些編譯器在遇到這種情況下,會警告與0比較無效。

3、整型常量
如果一個整型常量的第一個數字是0,那麼該常量將被視為八進位制數。因此10與010的含義截然不同。
有時候在上下文中為了格式對齊的需要,可能無意中將十進位制數寫成了八進位制,例如:

struct
 {
 	int part_number;
 	char *description;
 }parttab[]=
 {
 	046,"left-handed widget",
 	047,"right-handed widget",
 	125,"frammis"
 };

4、理解函式宣告

(*(void(*)())0)();

如上所示的語句,一般很難看懂,可以利用下面方法來解析:構造這類表示式其實只有一條簡單的規則:按照使用的方式來宣告。
任何C語言變數的宣告都是由兩部分組成:型別以及一組類似表示式的宣告符。宣告符從表面上看與表示式有些類似,對它求值應該返回一個宣告中給定型別的結果。簡單的宣告符為單個變數:

float f,g;

因為宣告符與表示式相似,可以在宣告符中任意使用括號:

float ((f));

同樣的邏輯也適用於函式和指標型別的宣告:

float ff();

這個宣告的含義,表示式ff()求值的結果是一個浮點數,ff是一個返回值為浮點型別的函式。

float *pf;

類似的,這個宣告的含義表示*pf是一個浮點數,也就是說,pf是一個指向浮點數的指標。
以上這些形式在宣告中可以組合起來。

float *g(),(*h)();

上式表明*g()與(*h)()是浮點表示式。*g()中g是一個,該函式的返回值型別為指向浮點數的指標。(*h)()中h是一個函式指標,h所指向的函式的返回值為浮點型別。

在瞭解如何宣告一個給定型別的變數,那麼該型別的型別轉換符就很容易得到:只需要把宣告中的變數名和宣告末尾的分好去掉,再將剩餘的部分用一個括號整個“封裝”起來即可:例如

float (*h)();

表示h是一個指標函式,h是一個指向返回值為float型別的函式的指標,h所指向的函式的返回值是float型別。

(float (*)());

表示一個“指向返回值為浮點型別的函式的指標”的型別轉換符。

現在,開始利用兩步分析 (*(void(*)())0)();
step1:假定變數fp是一個函式指標,則呼叫fp所指向的函式方法如下:

(*fp)();

因為fp是一個函式指標,故*fp就是該指標所指向的函式,所以(*fp)()就是呼叫該函式的方式。ANSIC標準允許程式設計師將上式簡寫為fp(),但是要記住這種寫法只是一個簡寫形式。

step2:找到一個恰當的表示式來替換fp,最簡單的轉換如下:

(*0)();

但是,上式並不能生效,因為運算子*必須要一個指標來做運算元,這個指標應該是一個函式指標,這樣經運算子作用後的結果才能夠作為函式被呼叫。因此,必須對上式中的0作型別轉換,轉換後的型別可以大致描述為:指向返回值為void型別的函式的指標。

若fp是指向返回值為void型別的函式的指標,那麼(*fp)()的值為void,fp的宣告如下:

void (*fp)();

因此,可以用下式來完成呼叫儲存位置為0的子例程:

void (*fp)();
  (*fp)();

下面對一個常數進行型別轉換,將其轉換為該變數的型別:只需要在變數宣告中將變數名去掉即可。因此,將常數0轉換為“指向返回值為void的函式的指標”型別:

(void (*)())0;

因此,可以用(void (*)())0;來替換fp:

(*(void (*)())0)();

注:利用typedef能夠使表示更加清晰:

typedef void (*funcptr)();
(*(funcptr)0)();

5、運算子優先順序

  while (c=getc(in) != EOF)
  	putc(c,out);

因為關係運算符的優先順序要高於賦值運算子,所以上式c的實際值是函式getc(in)的返回值與EOF比較的結果。因此,最後得到的檔案中只是包括了一組二進位制值為1的位元組流。
正確寫法:

while ((c=getc(in)) != EOF)
      	putc(c,out);

6、函式呼叫
如果函式f是一個函式:

f();

是一個函式呼叫語句;

f;

是一個什麼都不做的語句,更準確的說,這個語句是計算f的地址,卻並沒有呼叫f函式。

7、指標與陣列
c語言中的陣列注意的地方有一下兩點:
(1)c語言中陣列只有一維陣列,而且陣列大小必須在編譯期間就作為一個常數確定下來。然而陣列中的元素可以是任何型別的物件,當然可以是另外一個數組。這樣就可以很容的形成一個多維陣列。
(2)對於一個數組,只有兩個操作:確定該陣列的大小,以及獲得指向該陣列下標為0的元素指標。其他有關陣列的操作,即使看上去是以陣列下標進行運算的,實際上都是通過指標進行的。

int array[12][31];

這個語句聲明瞭array是一個數組,該陣列擁有12個數組型別的元素,每個元素是一個擁有31個整型元素的陣列。

sizeof(array)=31*12=372;

如果array不是用於sizeof的運算元,而是用於其他場合。那麼array總是被轉換成一個指向array陣列的起始元素的指標。

array[4]表示一個有著31個整形元素的陣列。

sizeof(array[4])=31*sizeof(int)=31*4=124;

指標p指向了陣列array[4]中下標為0的元素:

int *p;
p=array[4];

整型i得到陣列array某個位置的值:

int i;
i=array[4][7];
i=*(array[4]+7);
i=*(*(array+4)+7);

下式是錯誤的:因為array是一個二維陣列,即陣列的陣列,在此處array會將其轉換成一個指向陣列的指標;而p是一個指向整型變數的指標,將一種型別的指標賦值到另一種型別的指標是非法的。

p=array;

若要宣告一種指向陣列的指標的方法,如下:

int (*ap)[31];

上述語句聲明瞭*ap是一個擁有31個整型元素的陣列,ap就是一個指向這樣陣列的指標。因此:

int array[12][31];
int (*parray)[31];
parray=array;

這樣,parray將指向array的第一個元素。

8、指標大小

char *pc;
sizeof(pc);

在32位機器中是4位元組;
在64位機器中是8位元組;

在這裡插入圖片描述
9、if(3 == i){...}
如此寫法可以避免 將關係運算符寫成賦值運算子,避免bug;

10、malloc動態申請的內容,釋放後,將變數指向地址為空的地方會更加安全

int *pmal=(int *)malloc(100*sizeof(int));
...
...
...
if(pmal)
{
	free(pmal);
	pmal=NULL;
}