void myMalloc(char *s) //我想在函数中分配内存,再返回 { s=(char *) malloc(100); } void main() { char *p=NULL; myMalloc(p); //这里的p实际还是NULL,p的值没有改变,为什么? if(p) free(p); } 程序2:void myMalloc(char **s) { *s=(char *) malloc(100); } void main() { char *p=NULL; myMalloc(&p); //这里的p可以得到正确的值了 if(p) free(p); } | |
下面我们来深度分析一下:
关于程序1,myMalloc(char *s),当我们在main()中调用该函数时候,编译器会为函数的每个参数制作临时副本(引用除外),因此指针s的副本为_s,编译器则让
_s=s,如果函数体改变了_s所指向的内容,则s所指向的内容也随之改变,这也是指针可以做参数的原因,但是在本例中,_s申请了新的内存,则_s指向了新内存,
换言之,_s本身的指向地址变了,此时s跟_s所指向的是不同的内存地址,这样s所指向的内容一直没变,每调用该函数一次,会再堆上申请一块内存,长期这样会
导致内存泄露,后果是很严重的。
那么myMalloc(char **s)为什么又是可行的呢,道理是一样的,这是一个二级指针,编译器同样会复制该二级指针_s,由上面的分析过程,一级指针所指向的地址变了,
但是我们二级指针_s与s所指向的一级指针都没发生变化,变的是一级指针指向的内存,这样_s,s都可以通过该一级指针找到那块内存。这样可以达到我们的要求
补充一句,如果真想利用一级指针申请动态内存,可以改动该函数的返回值,即可。
下面我们再来看一个返回栈指针的例子。:
程序3:char *GetString(void) { char p[] = "hello world"; return p; // 编译器将提出警告 } void Test4(void) { char *str = NULL; str = GetString(); // str 的内容是垃圾 cout<< str << endl; } | |
程序4:char *GetString2(void) { char *p = "hello world"; return p; } void Test5(void) { char *str = NULL; str = GetString2(); cout<< str << endl; } | |
程序3返回的是栈内存的指针,一旦函数执行完毕,该指针也就是漂浮指针了。但是为什么程序4能够达到我们的要求,因为char *p="hello world",这里是在静态存储区域
申请的一块内存,在程序运行的过程中,一直存在,但是有个缺陷就是他是个只读的区域块。