1. 程式人生 > >[轉]C語言常見錯誤總結1

[轉]C語言常見錯誤總結1

process log item 它的 too 問題 程序 副本 proc

指針與數組的對比
c程序中,指針和數組在不少地方可以相互替換著用,讓人產生一種錯覺,以為兩者是等價的

數組要麽在靜態存儲區被創建(如全局數組),要麽在棧上被創建。數組名對應著(而不是指向)一塊內存,其地址與容量在生命周期內保持不變,只有數組的內容可以改變

指針可以隨時指向任意類型的內存塊,它的特征是“可變”,所以我們常用指針來操作動態內存。指針遠比數組靈活,但也更危險。

修改內容
字符數組a的容量是6個字符,其內容為hello。a的內容可以修改,例如a[0]=‘x‘.指針p指向常量字符串“world”(位於靜態存儲區,內容為world),常量字符串的內容是不可以被修改的。從語法上看,編譯器並不覺得語句p[0]=‘x‘有什麽不妥,但是該語句企圖修改常量字符串的內容而導致運行錯誤

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #include <stdio.h> int main() { char a[] = "hello"; a[0] = ‘x‘; printf("%s\n", a); char *p = "wrold"; p[0] = ‘x‘; printf("%s\n", p); return 0; }


內容復制與比較
不能對數組名進行直接復制與比較。若想把數組a的內容復制給數組b,不能用語句 b = a,否則將產生編譯錯誤。應該用標準庫函數strcpy進行復制。同理,比較b和a的內容是否相同,應該用標準庫函數strcmp進行比較

語句p = a並不能把a的內容復制指針p,而是把a的地址賦給了p。要想復制a的內容,可以先用庫函數malloc為p申請一塊容量為strlen(a)1個字符的內存,再用strcpy進行字符串復制。同理,語句if(p == a)比較的不是內容而是地址,應該用庫函數strcmp來比較

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 #include <stdio.h> #include <string.h> #include <stdlib.h> int main()
{ char a[] = "hello"; char b[10]; strcpy(b, a); //不能用b = a int len = strlen(a); char *p = (char *)malloc((len + 1) * sizeof(char)); strcpy(p, a); if (strcmp(p, a) == 0) { printf("p和a是相等的!\n"); } free(p); return 0; }


計算內存容量
用運算符sizeof可以計算出數組的容量(字節數)。sizeof(a)的值是12.指向p指向a,但是sizeof(p)的值卻是4.這是因為sizeof(p)得到的是一個指針變量的字節數(32bit機器內存地址為32bit),相當於sizeof(char *),而不是p所指的內存容量。

註意當數組作為函數的參數進行傳遞時,該數組自動退化為同類型的指針。不論數組a的容量是多少,sizeof(a)始終等於sizeof(char *)

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 #include <stdio.h> #include <string.h> #include <stdlib.h> void funC(char *a); int main() { char a[] = "hello"; char *p = a; printf("%d\n", sizeof(a)); // 6字節 printf("%d\n", sizeof(p)); // 4字節 funC(a); return 0; } void funC(char *a) { printf("%d\n", sizeof(a)); // 4字節而不是6字節 }


運算結果:

技術分享圖片

指針參數是如何傳遞內存的
如果函數的參數是一個指針,不要指望用該指針去申請動態內存。示例中,Test函數的語句GetMemory(str, 200)並沒有使str獲得期望的內存,str依舊是NULL,為什麽?

代碼

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 #include <stdio.h> #include <string.h> #include <stdlib.h> void GetMemory(char *p, int num) { p = (char*)malloc(sizeof(char) * num); } char* getMemory(char *p, int num) { p = (char *)malloc(sizeof(char) * num); return p; } int main() { char *str = NULL; str = getMemory(str, 200); strcpy(str, "hello world!"); //運行錯誤 printf("%s", str); free(str); return 0; }


錯誤

技術分享圖片

原因
問題出在函數GetMemory中。編譯器總是要為函數的每個參數制作臨時副本,指針參數p的副本是_p,編譯器使_p = p.如果函數體內的程序修改了_p的內容,就導致參數p的內容作相應的修改。這就是指針可以用作輸出參數的原因。在本例中,_p申請了新的內存,只是把_p所指的內存地址改變了,但是p絲毫未變。所以函數GetMemory並不能輸出任何東西。事實上,每執行一次GetMemory就會泄漏一塊內存,因為沒有用free釋放內存

改進
我們可以用函數返回值來傳遞動態內存,這種方法更簡單,見示例:

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 #include <stdio.h> #include <string.h> #include <stdlib.h> void GetMemory(char *p, int num) { p = (char*)malloc(sizeof(char) * num); } char* getMemory(char *p, int num) { p = (char *)malloc(sizeof(char) * num); return p; } int main() { char *str = NULL; str = getMemory(str, 200); strcpy(str, "hello world!"); //運行錯誤 printf("%s\n", str); free(str); return 0; }


註意:
用函數返回值來傳遞動態內存這種方法雖然好用,但是常常有人把return語句用錯了。這裏強調不要用return語句返回指向”棧內存“的指針,因為該內存在函數結束時自動消亡。

示例:

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 #include <stdio.h> #include <string.h> #include <stdlib.h> void GetMemory(char *p, int num) { p = (char*)malloc(sizeof(char) * num); } char* getMemory(char *p, int num) { p = (char *)malloc(sizeof(char) * num); return p; } char* getArray(void) { char p[] = "hello world!"; return p; // 編譯器提出警告 } int main() { char *str = NULL; str = getArray(); printf("%s\n", str); // str指向的內容是垃圾 free(str); return 0; }

杜絕“野指針”
"野指針"不是NULL指針,是指向“垃圾”內存的指針。人們一般不會錯用NULL指針,因為用if語句很容易判斷。但是“野指針”是很危險的,if語句對它不起作用。“野指針”的成因主要有兩種:

指針變量沒有初始化。任何指針變量剛被創建時不會自動成為NULL指針,它的缺省值是隨機的,它會亂指一氣。所以,指針變量在創建的同時應該被初始化,要麽將指針設置為NULL,要麽讓它指向合法的內存,例如:

?
1 2 char *p = NULL; char *str = (char *)malloc(sizeof(char) * 100);

指針p被free或者delete之後,沒有置為NULL,讓人誤以為p是個合法的指針
指針操作超越了變量的作用範圍


內存耗盡怎麽辦
如果在申請動態內存時找不到足夠大的內存塊,malloc函數將返回NULL指針,宣告內存申請失敗。通常有三種方式處理“內存耗盡”問題

判斷指針是否為NULL,如果是則馬上用return語句終止本函數。例如:

?
1 2 3 4 5 6 7 char* getPoint() { char *p = malloc(sizeof(char) * 100); if (p == NULL) { return null; } }

判斷指針是否為NULL,如果是則馬上用exit(1)終止整個程序的運行(我經常用也是推薦做法):

?
1 2 3 4 5 6 7 char* getPoint() { char *p = malloc(sizeof(char) * 100); if (p == NULL) { exit(1); } }

為new和malloc設置異常處理函數

[轉]C語言常見錯誤總結1