看下面一段程序:
- #include <stdio.h>
- #include <string.h>
- #include <stdlib.h>
- void get_string( char * p );
- int main(int argc, char **argv )
- {
- unsigned int i;
- char *p=NULL;
- get_string( p );
- /* print the string pointed to by p one character at a time */
- for( i=0; i < strlen(p); i++ ) {
- printf( "p[%d] = '%c'/n", i, p[i] );
- }
- exit( 0 );
- }
- void get_string( char * p )
- {
- /* make p point to the string "foobar" */
- memcpy( p, "foobar", strlen( "foobar")+1 );
- printf( "p's value is %p; It points to the string: /"%s/"./n", p, p);
- }
要求:用GDB找出程序中的兩處BUG。
直接編譯後,運行出現“段錯誤”,即“Segmenation fault (coredumped))”。
用GDB單步運行:
- (gdb) l
- 1 #include <stdio.h>
- 2 #include <string.h>
- 3 #include <stdlib.h>
- 4
- 5 void get_string( char * p );
- 6
- 7 int main(int argc, char **argv )
- 8 {
- 9 unsigned int i;
- 10 char *p=NULL;
- (gdb) break 9
- Breakpoint 1 at 0x8048406: file aa.c, line 9.
- (gdb) r
- Starting program: /home/ysong/a.out
- Breakpoint 1, main () at aa.c:10
- 10 char *p=NULL;
- Missing separate debuginfos, use: debuginfo-install glibc.i686
- (gdb) info locals
- i = 3215191144
- p = 0x79bff4
- (gdb) n
- 12 get_string( p );
- (gdb) info locals
- i = 3215191144
- p = 0x0
- (gdb) n
- Program received signal SIGSEGV, Segmentation fault.
- 0x0804847f in get_string (p=0x0) at aa.c:25
- 25 memcpy( p, "foobar", strlen( "foobar")+1 );
- (gdb) backtrace
因爲有char *p=NULL;
所以指針被初始化爲指向地址0x0。這是違規訪問內存地址,所以會出現段錯誤。
這裏用malloc函數動態分配內存空間:
- void get_string( char * p );
- int main(int argc, char **argv )
- {
- unsigned int i;
- char *p=(char*)malloc(100*sizeof(char));
- get_string(p);
- /* print the string pointed to by p one character at a time */
- for( i=0; i < strlen(p); i++ ) {
- printf( "p[%d] = '%c'/n", i, p[i] );
- }
- free(p);
- exit( 0 );
- }
- void get_string( char * p )
- {
- /* make p point to the string "foobar" */
- memcpy( p, "foobar", strlen( "foobar")+1 );
- printf( "p's value is %p; It points to the string: /"%s/"./n", p, p);
- }
編譯後的運行結果爲:
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;
}
需要包含頭文件:
#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 所得。