[轉]C語言常見錯誤總結1
指針與數組的對比
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