在C語言中,指針的重要性不言而喻,但在很多時候指針又被認為是一把雙刃劍。一方面,指針是構建數據結構和操作內存的精確而高效的工具。另一方面,它們又很容易誤用,從而產生不可預知的軟件bug。下面總結一下指針使用的常見錯誤。
一、使用未初始化的指針
這個錯誤很常見,指針未初始化時,系統會給指針分配個隨機地址,示例如下:
int *p; //或者 int *p = NULL;
···
*p = 10; //錯誤,指針未初始化
上述程序將值10寫到未知的內存位置,如果p指向系統內存空間,這樣很可能把系統本來地址里的內容給覆蓋了,會導致程序或者系統的崩潰。
二、沒有釋放內存
在堆中開辟內存以后,使用完成必須釋放內存,否則會造成內存泄漏,示例如下:
int *p = (int *)malloc(100);
···
free(p);
p = NULL;
三、不斷修改內存指針變量
很多時候使用指針開辟了內存空間,然后如果對指針指向進行改變操作,操作完成后直接釋放內存,會釋放了不該釋放的位置;另外程序丟失了對已開辟內存空間的控制,造成內存泄漏,示例如下:
//這種時候一般會定義兩個指向同一個開辟的內存空間的指針變量,一個用於操作,一個用於釋放,避免造成內存泄漏
char *p = (char *)malloc(100);
strcpy(p, "abcdefg");
p += 1; //對指針指向進行改變操作
*p = 'a';
free(p);
p = NULL;
四、注意"野指針"
"野指針"不是NULL指針,是指向“垃圾”內存的指針。人們一般不會錯用NULL指針,因為用if語句很容易判斷。但是“野指針”是很危險的,if語句對它不起作用。“野指針”的成因主要有兩種:
第一種,指針變量沒有初始化。任何指針變量剛被創建時不會自動成為NULL指針,它的缺省值是隨機的,它會亂指一氣。示例如下:
char *p; //錯誤,未初始化,成為野指針
第二種,指針變量被free或者delete之后,沒有置為NULL,讓人誤以為是個合法的指針。示例如下:
char *p = (char *)malloc(100);
...
free(p); //錯誤,沒有置為NULL,成為野指針
五、指針參數申請內存的常見錯誤
如果函數的參數是一個指針,不要指望用該指針去申請動態內存,示例如下:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void getMemory(char *p, int num)
{
p = (char*)malloc(sizeof(char) * num);
}
int main()
{
char *pStr = NULL;
pStr = getMemory(pStr, 200);
strcpy(pStr, "hello world!"); //錯誤,pStr仍然是空指針
printf("%s", pStr);
free(pStr);
pStr = NULL;
return 0;
}
上面getMemory(str, 200)
並沒有使pStr獲得期望的內存,pStr依舊是NULL,為什么?
問題出在getMemory
函數中。編譯器總是要為函數的每個參數制作臨時副本,指針參數p的副本是_p,編譯器使_p = p。在本例中,_p申請了新的內存,只是把_p所指的內存地址改變了,但是p絲毫未變,仍然是空指針。事實上,每執行一次getMemory
函數就會泄漏一塊內存,因為沒有用free釋放內存。
指針參數申請內存的兩種正確用法
第一種用法:使用函數返回值來傳遞動態內存,示例如下:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char* getMemory(char *p, int num)
{
p = (char *)malloc(sizeof(char) * num);
return p;
}
int main()
{
char *pStr = NULL;
pStr = getMemory(pStr, 200);
strcpy(pStr, "hello world!");
printf("%s", pStr);
free(pStr);
pStr = NULL;
return 0;
}
這里強調不要用return語句返回指向”棧內存“的指針,因為”棧內存“指針在函數結束時已經被自動回收。
第二種用法:使用指向指針的指針作為參數來傳遞動態內存,示例如下:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void getMemory(char **p, int num)
{
*p = (char *)malloc(sizeof(char) * num);
}
int main()
{
char *pStr = NULL;
getMemory(&pStr, 200);
strcpy(pStr, "hello world!");
printf("%s", pStr);
free(pStr);
pStr = NULL;
return 0;
}
在需要修改指針變量本身的時候,需要使用指向指針的指針作為參數,這也是傳值與傳地址的差別所在。
參考: