從一個簡單程序的GDB調試看段錯誤,指針的初始化及內存分配(malloc/free)

從一個簡單程序的GDB調試看段錯誤,指針的初始化及內存分配(malloc/free)

分類: C++ 2544人閱讀 評論(0) 收藏 舉報

看下面一段程序:

 

  1. #include <stdio.h>  
  2. #include <string.h>  
  3. #include <stdlib.h>  
  4. void get_string( char * p );  
  5. int main(int argc, char **argv )  
  6. {  
  7.     unsigned int i;  
  8.     char *p=NULL;  
  9.     get_string( p );  
  10.     /* print the string pointed to by p one character at a time */  
  11.     for( i=0; i < strlen(p); i++ ) {  
  12.         printf( "p[%d] = '%c'/n", i, p[i] );  
  13.     }  
  14.     exit( 0 );  
  15. }  
  16. void get_string( char * p )  
  17. {  
  18.     /* make p point to the string "foobar" */  
  19.     memcpy( p, "foobar", strlen( "foobar")+1 );  
  20.     printf( "p's value is %p; It points to the string: /"%s/"./n", p, p);  
  21. }  

要求:用GDB找出程序中的兩處BUG。

 

直接編譯後,運行出現“段錯誤”,即“Segmenation fault (coredumped))”。

用GDB單步運行:

  1. (gdb) l  
  2. 1       #include <stdio.h>  
  3. 2       #include <string.h>  
  4. 3       #include <stdlib.h>  
  5. 4  
  6. 5       void get_string( char * p );  
  7. 6  
  8. 7       int main(int argc, char **argv )  
  9. 8       {  
  10. 9           unsigned int i;  
  11. 10          char *p=NULL;  
  12. (gdb) break 9  
  13. Breakpoint 1 at 0x8048406: file aa.c, line 9.  
  14. (gdb) r  
  15. Starting program: /home/ysong/a.out   
  16. Breakpoint 1, main () at aa.c:10  
  17. 10          char *p=NULL;  
  18. Missing separate debuginfos, use: debuginfo-install glibc.i686  
  19. (gdb) info locals  
  20. i = 3215191144  
  21. p = 0x79bff4   
  22. (gdb) n  
  23. 12          get_string( p );  
  24. (gdb) info locals  
  25. i = 3215191144  
  26. p = 0x0  
  27. (gdb) n  
  28. Program received signal SIGSEGV, Segmentation fault.  
  29. 0x0804847f in get_string (p=0x0) at aa.c:25  
  30. 25          memcpy( p, "foobar", strlen( "foobar")+1 );  
  31. (gdb) backtrace  

因爲有char *p=NULL;

所以指針被初始化爲指向地址0x0。這是違規訪問內存地址,所以會出現段錯誤。

這裏用malloc函數動態分配內存空間:

  1. void get_string( char * p );  
  2. int main(int argc, char **argv )  
  3. {  
  4.           unsigned int i;  
  5.           char *p=(char*)malloc(100*sizeof(char));  
  6.           get_string(p);  
  7.         /* print the string pointed to by p one character at a time */  
  8.     for( i=0; i < strlen(p); i++ ) {  
  9.         printf( "p[%d] = '%c'/n", i, p[i] );  
  10.     }  
  11.         free(p);  
  12.     exit( 0 );  
  13. }  
  14. void get_string( char * p )  
  15. {  
  16.     /* make p point to the string "foobar" */  
  17.     memcpy( p, "foobar", strlen( "foobar")+1 );  
  18.     printf( "p's value is %p; It points to the string: /"%s/"./n", p, p);  
  19. }   

編譯後的運行結果爲:

p's value is 0x9cdd008; It points to the string: "foobar".
p[0] = 'f'
p[1] = 'o'
p[2] = 'o'
p[3] = 'b'
p[4] = 'a'
p[5] = 'r'
當然,這裏p的值是動態隨機的。

 

 

參考文章:

指針初始化引出的問題

 

小結如下:
(1)先看例子:
#include <iostream.h>

void main()
{
char *p,*p1="hello first!";
while((*(p++) = *(p1++)) != 0);

cout<<p<<endl;
}錯處:p定義時沒有初始化,p是指向不定,是一個野指針。
p++可能會引用非法的內存塊。
編譯時不會出錯,但運行時會造成程序崩潰。

(2)把上面的p初始化爲NULL
#include <iostream.h>

void main()
{
char *p=NULL,*p1="hello first!";
while((*(p++) = *(p1++)) != 0);

cout<<p<<endl;
}//也錯,NULL表示沒有任何指向。p++沒有任何意義,運行時會造成程序崩潰。這裏就應該想到不能對NULL的指針進行直接操作。


(3)現在爲p初始化爲" ":
void main()
{
char *p=" ",*p1="hello first!";

while((*(p++) = *(p1++)) != 0);

cout<<p<<endl;
}//錯:p指向的是一個const常量的內容,可以通過*(p++)形式引用該值,但是不能改變它指向const的內容。
(4)#include <iostream.h>
#include <string.h>
void main()
{
 char c[]="";
 char *p=c,*p1="hello first!";
 char *p_start=p;
 while((*(p++) = *(p1++)) != 0);
 
 cout<<c<<endl;
}//此時數組是一系列的變量了,也就是p一有指向,二不是指向常量的而是指向變量的。所以按理應該行的。問題出在c太小,造成了數組越界,所以錯掉!把c的大小改來不比"hellofirst!"小就行了。
(5)對於的就想到用new來初始化了:
#include <iostream.h>
#include <string.h>
void main()
{
 char *p,*p1="hello first!";
 p=new char;
 char *p_start=p;
 while((*(p++) = *(p1++)) != 0);
 
 cout<<p_start<<endl;
}//現在就可以了,哈,不過,我認爲在new時最好還是把它的大小給指出來,
如newchar[strlen(p1)+1];如果不指定大小,我想p++會指向一些已用得地址,而這些地址又不能隨便改,就會造成諸如野指針樣程序崩潰了。

小結:對於前面的問題,不防這樣來寫:
#include <iostream.h>
#include <string.h>
void main()
{
 char *p,*p1="hello first!";
 p=new char[strlen(p1)+1];
 //p=new char;//覺得最好別這樣子,newchar只相當於得到一個char對
 char *p_start=p;//象,分配一個字符的空間。
 while((*(p++) = *(p1++)) != 0);
 
 cout<<p_start<<endl;
}

 


malloc()的用法:


需要包含頭文件:
#include 'stdlib.h'

函數聲明(函數原型):

void *malloc(int size);

說明:malloc 向系統申請分配指定size個字節的內存空間。返回類型是 void* 類型。void* 表示未確定類型的指針。C,C++規定,void* 類型可以強制轉換爲任何其它類型的指針。

從函數聲明上可以看出。malloc 和 new 至少有兩個不同: new 返回指定類型的指針,並且可以自動計算所需要大小。比如:

int *p;

p = new int; //返回類型爲int* 類型(整數型指針),分配大小爲 sizeof(int);

或: 

int* parr;

parr = new int [100];   //返回類型爲 int* 類型(整數型指針),分配大小爲 sizeof(int) *100; 

而 malloc 則必須由我們計算要字節數,並且在返回後強行轉換爲實際類型的指針。 

int* p; 

p = (int *) malloc (sizeof(int)); 

第一、malloc 函數返回的是 void * 類型,如果你寫成:p = malloc (sizeof(int)); 則程序無法通過編譯,報錯:“不能將 void* 賦值給 int * 類型變量”。所以必須通過 (int *) 來將強制轉換。

第二、函數的實參爲 sizeof(int) ,用於指明一個整型數據需要的大小。如果你寫成: 

int* p = (int *) malloc (1);

代碼也能通過編譯,但事實上只分配了1個字節大小的內存空間,當你往裏頭存入一個整數,就會有3個字節無家可歸,而直接“住進鄰居家”!造成的結果是後面的內存中原有數據內容全部被清空。

malloc 也可以達到 new [] 的效果,申請出一段連續的內存,方法無非是指定你所需要內存大小。

比如想分配100個int類型的空間:

int* p = (int *) malloc ( sizeof(int) * 100 ); //分配可以放得下100個整數的內存空間。

另外有一點不能直接看出的區別是,malloc 只管分配內存,並不能對所得的內存進行初始化,所以得到的一片新內存中,其值將是隨機的。

除了分配及最後釋放的方法不一樣以外,通過malloc或new得到指針,在其它操作上保持一致。

 

free用法

需要包含頭文件(和 malloc 一樣):

#include 'stdlib.h'

函數聲明:

void free(void *block);

即: void free(指針變量);

之所以把形參中的指針聲明爲 void* ,是因爲free必須可以釋放任意類型的指針,而任意類型的指針都可以轉換爲void *。

舉例:

int* p = (int *) malloc(4);  

*p = 100;

free(p); //釋放 p 所指的內存空間

或者:

int* p = (int *) malloc ( sizeof(int) * 100 ); //分配可以放得下100個整數的內存空間。

……

free(p);

free 不管你的指針指向多大的空間,均可以正確地進行釋放,這一點釋放比 delete/delete []要方便。不過,必須注意,如果你在分配指針時,用的是new或new[ ],那麼抱歉,當你在釋放內存時,你並不能圖方便而使用free來釋放。反過來,你用malloc 分配的內存,也不能用delete/delete[] 來釋放。一句話,new/delete、new[]/delete[]、malloc/free 三對均需配套使用,不可混用!

int* p = new int[100];

free(p);      //ERROR! p 是由new 所得。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章