內存管理
常見的內存錯誤及其對策
*內存未分配成功卻使用了它:
在使用內存之前檢查指針是否為NULL。如果指針p是函數的參數,那麼在函數的入口處用assert(p!=NULL)進行檢查 ;如果使用malloc或者new來申請內存,應該用if(p==NULL)或if(p!=NULL)進防錯處理。
*無論用何種方式創建數組,都別忘了賦初值,即便是賦零值也不可省略,不要嫌麻煩。
*內存分配成並且已經初始化,但操作越過了內存的邊界。
*內存洩露。動態內存的申請與釋放必須配對,申請與釋放的次數一定要相同。
*釋放了內存卻繼續使用它(三種情況):
(1)對象調用關系過於復雜。應重新設計數據結構,解決對象管理混亂局面
(2)函數注意不要返回指向“棧內存”的“指針”或者“引用”,該內存在函數體結束時會被銷毀
(3)使用free或delete釋放了內存後,沒有將指針設置為NULL。導致產生“野指針”。
7.3指針與數組的對比
*數組要麼在靜態存儲區被創建(如全局數組),要麼在棧上被創建。數組名對應著(而不是指向)一塊內存,其地址與容量在聲明周期內保持不變,只有數組的內容可以改變。
7.3.1修改內容
企圖修改常量字符串的內容導致運行錯誤。
7.3.2內容復制與比較
對數組進行復制:strcpy();
對數組進行比較:strcmp();
//指針
int len=strlen(a);
char *p=(char *)malloc(sizeof(char)*(len+1));
strcpy(p,a); //不要用p=a,
if(strcmp(p,a)==0) //不要用if(p==a),那是比較地址
7.3.3計算內存容量
char a[]="hello world"; //sizeof(a)的值為12(別忘了'\0')。
void Func(char a[100])
{
cout<<sizeof(a)<<endl;//4字節而不是100字節,當數組作為函數的參數進行傳遞時,該數組自動退化
//為同類型的指針。
}
7.4指針參數是如何傳遞內存的?
如果函數的參數是一個指針,不要指望用該指針去申請動態內存。
void GetMemory(char *p,int num)
{
p=(char *)malloc(sizeof(char)*num);
}
void Test(void)
{
char *str=NULL;
GetMemory(str,100);//str仍然為NULL
strcpy(str,"hello"); //運行錯誤
}
Test函數的語句GetMemory(str,200)並沒有使str獲得期望的內存,str依舊是NULL。
毛病出在GetMemory()中。編譯器總是要為函數的每個參數制作臨時副本,指針參數p的副本是_p,編譯器使_p=p。如果函數體內的程序修改了_p的內容,就導致參數p的內容作相應的修改。這就是指針可以用作輸出參數的原因。在上面代碼中,_p申請了新的內存,只是把_p所指的內存地址改變了,但是p絲毫未變。所以函數GetMemory()並不能輸出任何東西。事實上,每執行一次GetMemory()就會洩露一塊內存,因為沒有用free釋放內存。
如果非要用指針參數去申請內存,那麼應該改用"指向針針的指針"
void GetMemory2(char **p,int num)
{
*p=(char *)malloc(sizeof(char)*num);
}
void Test2(void)
{
char *str=NULL;
GetMemory(&str,100);//注意參數是&str
strcpy(str,"hello");
cout<<str<<endl;
free(str);
}
還可以用函數返回值來傳遞動態內存,這種方法更簡單。
char * GetMemory3(int num)
{
char *p=(char*)malloc(sizeof(char)*num);
return p;
}
void Test3(void)
{
char *str=NULL;
str=GetMemory3(100);
strcpy(str,"hello");
cout<<str<<endl;
free(str);
}
用函數返回值傳遞動態內存這種方法雖然好用,但是要注意不要用return語句返回指向“棧內存"的指針,因為該內存在函數結束時自動消亡。
char *GetString(void)
{
char p[]="hello world";
return p; //編譯器將提出警告
}
void Test4(void)
{
char *str=NULL;
str=GetString();//str的內容是垃圾,GetString()中的p指向的內存已經被釋放
cout<<str<<endl;
}
改為下面的代碼:
char *GetString2(void)
{
char *p="hello world";
return p;
}
void Test5(void)
{
char *str=NULL;
str=GetString2();
cout<<str<<endl;
}