C語言中容易出錯的幾個地方

總結下c語言中值得注意的地方:

1、逗號表達式。

 因爲實際開發中應用的不多,所以很多人對這個都容易忽視。

逗號表達式,比賦值運算符優先級還低,結合性爲左結合。

例子

#include <stdio.h>

void main()

{

int a=2,b=3,c=4,x,resulta,resultb;

resulta=(x=a+b),(b+c);//resulta=5,x=5

//resulta後面的賦值運算符優先級比,高,所以resulta=5。

resultb=(resulta=(x=a+b),(b+c));//resultb=7,x=5,resulta=5。

//resultb 保存的是整個逗號表示式的值。

printf("resulta = %d,resultb = %d, x=%d/n",resulta,resultb,x);

 

2、關係運算符中的一例

int a=1,b=2,c=2,t;

while(a<b<c){t=a;a=b;b=t;c--}//關係運算符左結合 而不是右結合,可以循環兩次

printf("/n%d,%d,%d",a,b,c);//輸出1,2,0

 

3、函數(指針參數)

void settozero(int *pvar)

{

    *pvar = 0;

}

//參數傳遞爲指針就可以把原參數的值修改爲0了。因爲傳遞的指針是一個變量的地址。

看下面兩個例子:

void interchange(int *u ,int *v)

{

  int temp;

 temp = *u;

*u = *v;

*v = temp;

}//可以實現交換*u與*v的值

void interchange(int *u ,int *v)

{

  int *temp;

temp = u;

 u = v;

v  = temp;

}//不能實現值的交換。

注意函數參數實際傳遞的爲實參的副本。如果參數爲指針形式,則也不例外,只是傳遞的指針副本與實際的參數指向的地址空間一樣,所以修改指針副本所指向的內存的內容也就算是修改了實參(指針)所指向的內存的內容。效果是一樣的。

引申:其實scanf("%d",&num);就用到了這層含義。讀取一個數值,然後將其存儲到通過參數獲得的地址中。

 

4、*p++  等價於 *(p++)     而不是(*p)++;由於*與++的優先級別相同,但是結合性爲從右至左。

 

5、函數中絕對不能返回局部變量的地址。

例子 1

long * IncomePlus(long *pPay)

{

 long pay = 0;

 pay  = *pPay + 10000;

return &pay;

}//這樣做就不對,因爲return的時候pay這個局部變量已經不在IncomePlus函數的作用域內。

例子 2

char * str_in(void)

{

  char buffer[BUFFER_LEN];

 char *pString = NULL;

 if(gets(buffer)  == NULL)

{

   return FALSE;

}

 pString = (char *) malloc( strlen(buffer) +1 );

if (pString == NULL)

{

   abort();

}

return  strcpy(pString , buffer);

}//這樣做就可以,但是需要注意內存釋放,因爲函數內部只是malloc了一塊內存,這就需要調用函數來釋放內存了。搞不好會內存泄露的。

 

6、注意union類型;其所佔內存大小爲sizeof(其成員)最大的一個。因爲所有成員都共享一塊內存。

 

7、函數指針的用法很靈活。

int (*pfun)(int ,int);

typedef  int (*function_pointer) (int ,int); //聲明一個函數指針類型

這樣就可以function_pointer pfun1;

                   function_pointer pfun2;  假設有函數sum,difference,則可以

                  function_pointer  pfun3 = sum;

                  function_pointer  pfun3 = difference;

 

更有意思的是函數指針做參數:

int sum(int ,int );

int difference(int ,int );

int any_function(int  (*pfun)(int ,int ),int x,int y);

void main()

{

int a = 10;

int b = 5;

int result = 0;

int (*pf)(int ,int ) = sum;

result = any_function(pf,a,b);

printf("reslut  = %d/n",result);

}

int any_function(int (*pfun)(int, int),int x,int y)

{

   return pfun(x,y);

}  

int sum(int x,int y)

{

return x + y;

}

//仔細理解下 就會發現函數指針太靈活太好用太重要了。

還有函數指針數組 例子

int (*pfun[2]) (int ,int ) = { sum,difference };

 

 8、注意long double的sizeof大小;很多書上介紹的都爲10,但是我計算機上運行結果爲8。可能這個與cpu有關,不同的機器運行結果會不同的。

 

9、結構中的位域

    雖然用的比較少,但是還是值得注意

 struct

{

 unsigned   int  flag1:1;

  unsigned    int  flag2:2;//佔2bits,所以範圍爲0~3

  unsigned    int  flag3:1;

}indic;

sizeof(indic) = ? //4

 

10、指向二維數組的指針常出錯的地方

 int  a[4][3] ;

   int *pa = a;//非法,雖然這樣寫大多時,運行結果也對。但是這樣寫是不合法的

 int *pa = *a;//合法,這樣兩邊就位於同一個層次了

    int  *pa = a[0];//合法

 int *pa = &a[0][0];//合法

 

11、注意數組指針與指針數組不要搞混淆了

 指針數組:就是指針的數組,數組的元素是指針,如 int *p[2];首先聲明瞭一個數組,數組裏的元素是int型的指針。

 數組指針:即指向數組的指針,如 int (*p) [2];聲明瞭一個指針,該指針指向一個有2個int型元素的數組。

 其實這兩種寫法主要是因爲運算符的優先級, 因爲[ ]的優先級比*高。所以第一種寫法,p先和[ ]結合,所以是一個數組,後與*結合,是指針。後一種寫法同理。
    指針數組如下處理就會很清楚:
    typedef int* intPtr;
    intPtr p[2];
    一目瞭然,所以爲了避免迷惑,做適當的typedef也是很有必要的。
    同理,數組指針也可以作類似處理:
    typedef int intArray2[2];
    intArray2 * p;
    和原來的聲明都是等價的。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章