晦澀的語法?還是優秀IDE的欠缺?
我想那都不是問題,最多的多是1個類似這樣的毛病:
這是新手沒法避免的毛病,也是老手極力躲避也常常遇到的毛病。
本篇,試圖簡略地剖析1段會引發這個毛病的程序,帶來1些啟發。
先看兩份代碼,1份是毛病的.
#include "string.h"
#include <stdlib.h>
#include <stdio.h>
void func1(char ** dest,char * src,int n) {
(*dest) = (char*)malloc(sizeof(char)*n);
strcpy(*dest,src);
}
int main(int argc,char** args) {
char ** p = NULL;
char str[] = "foreach_break";
int len = sizeof(str);
printf("%d
",len);
func1(p,str,len);
printf("%s
",*p);
free(p);
p = NULL;
}
#include "stdio.h"
#include "string.h"
#include "stdlib.h"
void func1(char ** dest,char * src,int n) {
(*dest) = (char*)malloc(sizeof(char)*n);
strcpy(*dest,src);
}
int main(int argc,char** args) {
char * p = NULL;
char str[] = "foreach_break";
int len = sizeof(str);
printf("%d
",len);
func1(&p,str,len);
printf("%s
",p);
free(p);
//p = NULL;
}
代碼意圖來自技術問答中的1個huffman樹不能運行的問題。
固然,我剝離掉了大部份關于huffman的部份,并略加改動。
它們最大的不同:
毛病代碼:
char ** p = NULL;
func1(p,str,len);
正確代碼:
char * p = NULL;
func1(&p,str,len);
或許你會奇怪,你看到“正確”的代碼中竟然注釋了這行:
//p = NULL;
同時,可能有人會覺得指針char ** p
向函數func1傳遞*p
,與指針char * p
向函數func1
傳遞&p
沒甚么不同啊?
嗯。這類想法也很有慣性,由于C語言沒有類似這樣的函數聲明:
void func(int &)
同時,對char * p
作&p
操作不也得到個char **
嗎?
那末,我們看看程序自己怎樣說?
如果你常常遇到段毛病,希望你仔細看明白上面的圖在說甚么。
所謂野指針,就是很野的指針,你不知道它指向了哪一個地址,也不知道對這個地址取值是不是會出錯,但,野指針也是指針,有1個寄存它的內存地址.
正確的代碼中,寄存指針
p
的地址是0x7fffffffddc0
;
毛病的代碼中,寄存指針p
的地址是0x7fffffffdd78
;
所謂零指針,就是指向了0x0的指針.對這個0x0取值是不是會出錯呢?你想想.
你對在正確的程序中注釋了p = NULL
而感到不解?
其實這沒甚么,取決于這個指針p
在后續代碼中怎樣使用.
free(p);
這句代碼的履行,會釋放掉指針p
所指向的內存地址,歸還給操作系統.
固然條件是這個地址確有所指、你也有權訪問它.
p = NULL;
這句代碼的履行,是讓指針p
指向了0x0
,變回了空指針.
雖然它指向的內存已被釋放,但是它還指向那個地址.
這就是懸浮指針,指向的地址已不可用確還指向,就不是確有所指.
由于我們的main
函數行將履行終了,所以在它返回后,寄存空指針p
的地址會被釋放.
由于指針
p
是main
函數的1個臨時變量.
所以我們可以毫無顧慮的注釋掉p = NULL
.
另外,如果free(p)
沒有被履行,而先履行了p = NULL
,那末p
原來指向的內存空間可能就沒法被正確釋放,如果再也沒有其它的援用指向了那塊地址,那塊地址就被遺忘在那里,同時不能被回收.
這個,叫內存泄漏 (Memory Leak).
現在,我們來看看函數func1
的調用。
首先,是C代碼:
然后是兩段代碼的對照:
你應當已看出了差別。
毛病的代碼的dest
參數傳入了0x0
.
接著是履行:
(*dest) = (char*)malloc(sizeof(char)*n);
這句代碼在進行(*dest)
時就會產生段毛病.
究其緣由,就在于char ** p = NULL
讓p
變成了零指針,*p
相當于對0x0
這個地址取值.
利用程序啟動時,操作系統會建立1個進程(process),這個進程具有自己獨立的地址空間,稱作虛擬地址空間(virtual memory space).
0x0
在這個空間中,不能被訪問.
試圖訪問1個不能被訪問的空間,就會段毛病.
段毛病的1種,我們探索終了.
現在你知道以下兩種操作的含義了嗎?
/* p指向的地址,對其取值,如果這個地址有東西,也有權取,沒問題.*/
char ** p -> *p; (1)
/* 寄存p的地址,或指向p的援用,這個地址必定有東西,所以沒問題*/
char * p -> &p (2);
本篇結束.