|
1、指針沒有指向一塊合法的內(nèi)存 定義了指針變量,但是沒有為指針分配內(nèi)存,即指針沒有指向一塊合法的內(nèi)存。 1.1、結(jié)構(gòu)體成員指針未初始化 struct student { char *name; int score; }stu,*pstu; int main() { strcpy(stu.name,"Jimy"); stu.score = 99; return 0; } 這里定義了結(jié)構(gòu)體變量 stu,只是給name 這個指針變量本身分配了 4 個字節(jié)。name指針并沒有指向一個合法的地址,這時候其內(nèi)部存的只是一些亂碼。所以在調(diào)用 strcpy函數(shù)時,會將字符串"Jimy"往亂碼所指的內(nèi)存上拷貝,而這塊內(nèi)存 name 指針根本就無權(quán)訪問,導致出錯。解決的辦法是為 name指針 malloc一塊空間。 同樣,也有人犯如下錯誤: int main() { pstu = (struct student*)malloc(sizeof(struct student)); strcpy(stu.name,"Jimy"); stu.score = 99; return 0; } 為指針變量 pstu分配了內(nèi)存,但是同樣沒有給name指針分配內(nèi)存。錯誤與上面第一種情況一樣,解決的辦法也一樣。這里用了一個 malloc給人一種錯覺,以為也給 name 指針分配了內(nèi)存。 1.2 沒有為結(jié)構(gòu)體指針分配足夠的內(nèi)存 int main() { pstu = (struct student*)malloc(sizeof(struct student)); strcpy(stu.name,"Jimy"); stu.score = 99; return 0; } 為 pstu分配內(nèi)存的時候,分配的內(nèi)存大小不合適。這里把sizeof(struct student)誤寫為sizeof(struct student*)。當然 name 指針同樣沒有被分配內(nèi)存。解決辦法同上。 2、指針分配的內(nèi)存不夠 為指針分配了內(nèi)存,但是內(nèi)存大小不夠,導致出現(xiàn)越界錯誤。 char *p1 = “abcdefg”; char *p2 = (char *)malloc(sizeof(char)*strlen(p1)); strcpy(p2,p1); p1是字符串常量,其長度為 7個字符,但其所占內(nèi)存大小為 8個 byte。初學者往往忘了字符串常量的結(jié)束標志“\0” 。這樣的話將導致 p1字符串中最后一個空字符“\0”沒有被拷貝到 p2中。解決的辦法是加上這個字符串結(jié)束標志符: char *p2 = (char *)malloc(sizeof(char)*strlen(p1)+1*sizeof(char)); 這里需要注意的是,只有字符串常量才有結(jié)束標志符。比如下面這種寫法就沒有結(jié)束標志符了: char a[7] = {‘a(chǎn)’,’b’,’c’,’d’,’e’,’f’,’g’}; 另外,不要因為 char類型大小為 1個 byte就省略 sizof(char)這種寫法。這樣只會使你的代碼可移植性下降。 3、內(nèi)存越界 內(nèi)存分配成功,且已經(jīng)初始化,但是操作越過了內(nèi)存的邊界。這種錯誤經(jīng)常是由于操作數(shù)組或指針時出現(xiàn)“多 1”或“少 1” 。比如: int a[10] = {0}; for (i=0; i<=10; i++) { a[i] = i; } 所以,for循環(huán)的循環(huán)變量一定要使用半開半閉的區(qū)間,而且如果不是特殊情況,循環(huán)變量盡量從 0 開始。 4、內(nèi)存泄漏 產(chǎn)生泄漏的內(nèi)存就是堆上的內(nèi)存,也就是說由malloc系列函數(shù)或 new操作符分配的內(nèi)存。如果用完之后沒有及時 free或delete,這塊內(nèi)存就無法釋放,直到整個程序終止。 malloc函數(shù)的返回值是一個 void類型的指針,參數(shù)為 int類型數(shù)據(jù),即申請分配的內(nèi)存大小,單位是 byte。內(nèi)存分配成功之后,malloc函數(shù)返回這塊內(nèi)存的首地址。你需要一個指針來接收這個地址。但是由于函數(shù)的返回值是 void *類型的,所以必須強制轉(zhuǎn)換成你所接收的類型。也就是說,這塊內(nèi)存將要用來存儲什么類型的數(shù)據(jù)。比如: char *p = (char *)malloc(100); 在堆上分配了 100個字節(jié)內(nèi)存,返回這塊內(nèi)存的首地址,把地址強制轉(zhuǎn)換成 char *類型后賦給 char *類型的指針變量 p。同時告訴我們這塊內(nèi)存將用來存儲 char類型的數(shù)據(jù)。也就是說你只能通過指針變量 p 來操作這塊內(nèi)存。這塊內(nèi)存本身并沒有名字,對它的訪問是匿名訪問。 使用 malloc函數(shù)成功分配一塊內(nèi)存的過程。但是,每次你都能分配成功嗎?如果所申請的內(nèi)存塊大于目前堆上剩余內(nèi)存塊(整塊) ,則內(nèi)存分配會失敗,函數(shù)返回 NULL。既然 malloc函數(shù)申請內(nèi)存有不成功的可能,那我們在使用指向這塊內(nèi)存的指針時,必須用 if(NULL !=p)語句來驗證內(nèi)存確實分配成功了。 既然有分配,那就必須有釋放。不然的話,有限的內(nèi)存總會用光,而沒有釋放的內(nèi)存卻在空閑。與 malloc對應的就是 free函數(shù)了。free函數(shù)只有一個參數(shù),就是所要釋放的內(nèi)存塊的首地址。比如上例: free(p); free函數(shù)看上去挺狠的,但它到底作了什么呢?其實它就做了一件事:斬斷指針變量與這塊內(nèi)存的關系。比如上面的例子,我們可以說 malloc函數(shù)分配的內(nèi)存塊是屬于 p的,因為我們對這塊內(nèi)存的訪問都需要通過 p 來進行。 free函數(shù)就是把這塊內(nèi)存和 p之間的所有關系斬斷。從此 p 和那塊內(nèi)存之間再無瓜葛。至于指針變量 p 本身保存的地址并沒有改變,但是它對這個地址處的那塊內(nèi)存卻已經(jīng)沒有所有權(quán)了。那塊被釋放的內(nèi)存里面保存的值也沒有改變,只是再也沒有辦法使用了。 malloc兩次只 free一次會內(nèi)存泄漏;malloc一次 free兩次肯定會出錯。也就是說,在程序中 malloc的使用次數(shù)一定要和 free相等,否則必有錯誤。這種錯誤主要發(fā)生在循環(huán)使用malloc函數(shù)時,往往把 malloc和 free次數(shù)弄錯了。 既然使用 free函數(shù)之后指針變量p 本身保存的地址并沒有改變, 那我們就需要重新把p的值變?yōu)?NULL:p = NULL;如果沒有把指針置 NULL,這個指針就成為了“野指針” ,也有書叫“懸垂指針” 。這是很危險的,而且也是經(jīng)常出錯的地方。所以一定要記住一條:free完之后,一定要給指針置 NULL |
|
|