在函数体内开辟动态内存时,函数形参选择指向指针的指针的原理解析
2013-04-21 11:50
344 查看
看到一道找错题,题目如下:
题目给出的答案是“
传入GetMemory( char *p )函数的形参为字符串指针,在函数内部修改形参并不能真正的改变传入形参的值,执行完
后的str仍然为NULL;”
这个答案用一句常识一笔带过,并没有做更深的讲解,我也没搜索到更满意的答案,就自己思考了一下,想到了一个较为形象的理解,可供参考。
首先,函数GetMemory的形参p是一个char *类型,它是实参str的一个副本,也就是说,当调用GetMemory函数的时候,创建p,与此同时,将str的“值”即str指向的内存首地址赋值给p,p和str指向同一块内存区域,然后才是函数体的处理,这个处理的过程有一定的迷惑性,也是理解这个题目的关键:函数体申请了一块新的动态内存,(注意:这块新申请的内存与原来str和p指向的那块内存无关)然后,将申请的这块内存的首地址赋值给形参p,这个时候有意思啦,str指向的内存还是老样子,但形参p已经改变了指向,也就是说p的指向与str的指向已经不同,这其实是导致Test函数中错误的根源。
如果GetMemory的形参是char **p的形式,p指向的是一个(指向char型变量的)指针,传入实参需要&str才能匹配,调用GetMemory的流程是:创建指针p,然后将str所在内存的首地址赋值给它,此时,对(*p)的操作就是对str的操作,就可以避免题目中的错误。
2014-05-22补充
从下面的函数可以更好的理解相关内容:
当Test函数中调用GetMemory时,单步调试可以发现,GetMemory函数结束后,p被释放。从本质上讲,p仅仅是一个局部与函数的变量,而且是自动变量,所有局部变量当作用域结束的时候都会被释放。同时,这个过程中从pG的值可知,使用malloc开辟的内存并没有随着函数GetMemory的结束而释放,这正是malloc和free需要成对出现的原因:使用malloc开辟的内存必须手动释放,,而且可以在任何“可见”的位置释放,不一定局限于开辟它的函数内。
函数传参有三种:值传递、传地址、引用型形参,但值传递和传地址有一个共同本质:仅仅形参变量本身而言,都是值传递的副本形式,只是说当传地址的时候,形参开辟了一个实参的副本,指向了原本实参指向的内存。
另外,【标注①】只能通过调用strcpy函数来实现将一个字符串赋给一个字符数组,而不能直接用赋值语句将一个字符串常量或字符数组直接赋给一个字符数组。如下是两个例子:
第二个例子中使用字符串常量直接赋值给字符串指针的形式也可以输出正确结果,但是,当运行到free函数的时候,程序报错,提示“堆被破坏”,只有使用srtcpy才能正确运行。
第一个例子是《C++程序设计》(谭浩强版)第154页的的原始内容,也正是刚刚复习过这章的内容,所以当运行第二个例子的时候,很快发现了问题出在哪里
2014-06-26
另外在《C++程序设计》(谭浩强版)第173页有下面一句话:
调用函数时,不会改变实参指针变量的值,但可以改变实参指针变量所指向变量的值。
所以,如果想在调用函数时改变实参指针变量的值,用指向指针的指针即可达到目的。
有很多人说谭版的《C++程序设计》很简单,看过以后没学到什么东西,这点我一直以来都不认同,我甚至觉得直至今日,它仍是我看过的C++相关书籍中最优秀的书籍,通俗易懂又细致缜密,有时候,读书不一定挑最难的读,正像《西游降魔篇》中一样,或许《真正的大日如来真经》正是那本《儿歌三百首》。
void GetMemory( char *p ) { p = (char *) malloc( 100 ); } void Test( void ) { char *str = NULL; GetMemory( str ); strcpy( str, "hello world" ); printf( str ); }
题目给出的答案是“
传入GetMemory( char *p )函数的形参为字符串指针,在函数内部修改形参并不能真正的改变传入形参的值,执行完
char *str = NULL; GetMemory( str );
后的str仍然为NULL;”
这个答案用一句常识一笔带过,并没有做更深的讲解,我也没搜索到更满意的答案,就自己思考了一下,想到了一个较为形象的理解,可供参考。
首先,函数GetMemory的形参p是一个char *类型,它是实参str的一个副本,也就是说,当调用GetMemory函数的时候,创建p,与此同时,将str的“值”即str指向的内存首地址赋值给p,p和str指向同一块内存区域,然后才是函数体的处理,这个处理的过程有一定的迷惑性,也是理解这个题目的关键:函数体申请了一块新的动态内存,(注意:这块新申请的内存与原来str和p指向的那块内存无关)然后,将申请的这块内存的首地址赋值给形参p,这个时候有意思啦,str指向的内存还是老样子,但形参p已经改变了指向,也就是说p的指向与str的指向已经不同,这其实是导致Test函数中错误的根源。
如果GetMemory的形参是char **p的形式,p指向的是一个(指向char型变量的)指针,传入实参需要&str才能匹配,调用GetMemory的流程是:创建指针p,然后将str所在内存的首地址赋值给它,此时,对(*p)的操作就是对str的操作,就可以避免题目中的错误。
2014-05-22补充
从下面的函数可以更好的理解相关内容:
char *pG=NULL; //全局变量 void GetMemory(char *p) { p=(char *)malloc(100); pG=p; }
当Test函数中调用GetMemory时,单步调试可以发现,GetMemory函数结束后,p被释放。从本质上讲,p仅仅是一个局部与函数的变量,而且是自动变量,所有局部变量当作用域结束的时候都会被释放。同时,这个过程中从pG的值可知,使用malloc开辟的内存并没有随着函数GetMemory的结束而释放,这正是malloc和free需要成对出现的原因:使用malloc开辟的内存必须手动释放,,而且可以在任何“可见”的位置释放,不一定局限于开辟它的函数内。
函数传参有三种:值传递、传地址、引用型形参,但值传递和传地址有一个共同本质:仅仅形参变量本身而言,都是值传递的副本形式,只是说当传地址的时候,形参开辟了一个实参的副本,指向了原本实参指向的内存。
另外,【标注①】只能通过调用strcpy函数来实现将一个字符串赋给一个字符数组,而不能直接用赋值语句将一个字符串常量或字符数组直接赋给一个字符数组。如下是两个例子:
char str1[10],str2[]="China"; str1="China"; //错误:不能将一个字符串常量赋给字符数组 str1=str2; //错误:不能将一个字符数组的内容赋给另一个字符数组 strcpu(str1,"China"); //正确:
char * str=NULL; str=(char *)malloc(10); //str="China"; //此句错误 strcpy(str,"China"); //正确 free(str);
第二个例子中使用字符串常量直接赋值给字符串指针的形式也可以输出正确结果,但是,当运行到free函数的时候,程序报错,提示“堆被破坏”,只有使用srtcpy才能正确运行。
第一个例子是《C++程序设计》(谭浩强版)第154页的的原始内容,也正是刚刚复习过这章的内容,所以当运行第二个例子的时候,很快发现了问题出在哪里
2014-06-26
另外在《C++程序设计》(谭浩强版)第173页有下面一句话:
调用函数时,不会改变实参指针变量的值,但可以改变实参指针变量所指向变量的值。
所以,如果想在调用函数时改变实参指针变量的值,用指向指针的指针即可达到目的。
有很多人说谭版的《C++程序设计》很简单,看过以后没学到什么东西,这点我一直以来都不认同,我甚至觉得直至今日,它仍是我看过的C++相关书籍中最优秀的书籍,通俗易懂又细致缜密,有时候,读书不一定挑最难的读,正像《西游降魔篇》中一样,或许《真正的大日如来真经》正是那本《儿歌三百首》。
相关文章推荐
- 习题 8.19(1) 编写一个函数new,对n个字符开辟连续的存储空间,此函数应返回一个指针(地址),指向字符串开始的空间。new(n)表示分配n个字节的内存空间。
- C语言学习9: malloc动态内存存储,动态内存分配去空格字符增长版,动态内存分配去符号incr增长版,型参和返回值都是int型的函数的指针,main函数的地址也可以用指针指向,typedef定义函数指针,函数定义与嵌套的作用,返回函数指针类型,const作用
- 编写一个函数new,对n个字符开辟连续的存储空间,此函数应返回一个指针(地址),指向字符串开始的空间。new(n)表示分配n个字节的内存空间。
- 每日一C,指向一维、二维数组的指针及数组作为函数形参的思考(二)
- 指向指针的指针申请动态内存
- 解析指针数组,数组指针,函数指针,函数指针数组,指向函数指针数组的指针
- C语言基础之函数、虚拟键盘的使用、数组、指针、动态申请内存、内存泄漏
- C和指针之动态内存分配之(编写calloc函数,函数内部使用malloc函数来获取内存)
- 【原】函数返回指针,指向的内存谁来释放?
- 返回指针的函数及动态申请内存
- C语言基础之函数、虚拟键盘的使用、数组、指针、动态申请内存、内存泄漏
- 函数参数为指针,内部进行开辟内存问题
- qsort实现原理与应用 指向函数的指针
- C和指针之动态内存分配之(编写calloc函数,函数内部使用malloc函数来获取内存)
- 传递空指针,在函数中开辟内存并返回
- C++指向函数的指针实例解析
- 细嚼慢咽C++primer(3)——引用形参,内联函数,重载函数,指向函数的指针
- 函数调用缺少参数列表;请使用“&Student::Printf”创建指向成员的指针 问题解析
- c中动态开辟内存的几个函数
- 双重指针动态开辟内存