|
|
如果理解这个关于指针参数传递内存的论断
|
|
最近在看一篇文章,有个地方没理解:
如果函数的参数是一个指针,不要指望用该指针去申请动态内存。示例7-4-1中,Test函数的语句GetMemory(str, 200)并没有使str获得期望的内存,str依旧是NULL,为什么?
void GetMemory(char *p, int num)
{
p = (char *)malloc(sizeof(char) * num);
}
void Test(void)
{
char *str = NULL;
GetMemory(str, 100); // str 仍然为 NULL
strcpy(str, "hello"); // 运行错误
}
毛病出在函数GetMemory中。编译器总是要为函数的每个
参数制作临时副本,指针参数p的副本是 _p,编译器使
_p = p。如果函数体内的程序修改了_p的内容,就导致参
数p的内容作相应的修改。这就是指针可以用作输出参数
的原因。在本例中,_p申请了新的内存,只是把_p所指
的内存地址改变了,但是p丝毫未变。所以函数GetMemory
并不能输出任何东西。事实上,每执行一次GetMemory就
会泄露一块内存,因为没有用free释放内存。
程序里不是对p在操作吗,到底是对副本操作还是对正本操作?
|
|
|
Re: 如果理解这个关于指针参数传递内存的论断
|
|
这是关于函数参数是传值还是传址的问题
你的程序是传值,以下是可用de传址方式
void GetMemory(char **p, int num)
{
char *tmp;
tmp = (char *)malloc(sizeof(char) * num);
*p = (char*)tmp ;
}
void Test(void)
{
char *str = NULL;
GetMemory(&str, 100);
if(str){
strcpy(str, "hello");
printf("str = %s\n",str);
free(str);
}
}
|
|
|
Re: 如果理解这个关于指针参数传递内存的论断
|
|
对,那篇文章里有讲这个问题,我主要是对这段话不理解:
如果函数体内的程序修改了_p的内容,就导致参
数p的内容作相应的修改。这就是指针可以用作输出参数
的原因。在本例中,_p申请了新的内存,只是把_p所指
的内存地址改变了,但是p丝毫未变。
前面说改了一个,另一个会变,后面又说改—p,不影响p,怎么理解?谢了。
|
|
|
Re: 如果理解这个关于指针参数传递内存的论断
|
|
调用GetMemory函数的目的是是为了更改str的内容,原来是str变量所在地址存放的内容是NULL,现需要被赋给一块系统分配的可用空间的起始地址。
如果将str直接传入,是str的值被传入,这儿是NULL,对于GetMemory函数而言就相当于char *p = NULL,然后分配空间将地址赋给p,在函数结束时char *p这个栈中变量将被pop出,根本没有改变str的值。并且由于没有显示调用free,分配的空间得不到及时回收 ,产生所谓得泄漏。
于是要想改变str变量存放的内容,必需将Test()中str这个变量的地址&str传入,GetMemory中将malloc返回的地址赋给p指针所指向的内容,这里的p指针在调用该函数时被初始化为变量str的地址&str,于是str的内容就被更改了。
不好意思,罗嗦了这么多,还不知道有没有说清楚
你的C不是很熟,内核中的C部分源码还没有开始看吧?
|
|
|
Re: 如果理解这个关于指针参数传递内存的论断
|
|
对,还没开始,正准备,c也不熟,想熟,想从读kernel开始,一举两得。这不就有问题了,:-)
泄漏和指针传递问题我是了解的,主要是对他p和—p的那段论述看不懂:
《
编译器总是要为函数的每个参数制作临时副本,指针参数p的副本是 _p,编译器使_p = p。如果函数体内的程序修改了_p的内容,就导致参数p的内容作相应的修改。这就是指针可以用作输出参数的原因。在本例中,_p申请了新的内存,只是把_p所指的内存地址改变了,但是p丝毫未变。所以函数GetMemory并不能输出任何东西 》
1)参数的临时副本是什么意思?栈中增加了一个零时变量吗?
2) 前面说改了—p就改了p,后面又说—p指的内存地址改变了,但是p丝毫未变,什么意思?
|
|
|
Re: 如果理解这个关于指针参数传递内存的论断
|
|
1) 函数调用时会把传入的参数压入栈中,可谓临时变量
2)我觉得它的这段话,是针对两段不同的程序中的P而言,放到一起讲把内容搞混了
前者应该是这对这样的模型
GetMemory(char *p,..)
{
*p = 'a';/*程序修改了p的内容就导致参数p的内容作相应的修改*/
}
Test()
{
char c;
GetMemory(&c,..);
/*这儿c的内容已经在GetMemory函数中被修改*/
}
|
|
|
Re: 如果理解这个关于指针参数传递内存的论断
|
|
》是针对两段不同的程序中的P而言,放到一起讲把内容搞混了
还是你高,我没理解好。我还以为有什么高深的理论在里面。
|
|
|
Re: 如果理解这个关于指针参数传递内存的论断
|
|
1)指针的副本是什么意思?栈中增加了一个零时变量吗?
答:比如指针p 的内容是地址0x8000
p的副本p1也是一个指向0x8000的指针变量
2) 前面说改了—p就改了p,后面又说—p指的内存地址改变了,但是p丝毫未变,什么意思?
答: 指针做函数参数时函数在堆栈上分配一个指针变量p1它的内容和参数p指向同一内存地址。当p1指向的地址改变后。p指向的地址并没有改变,指针p1是一个堆栈变量随着函数调用结束就会释放,而p1指向的地址空间就会变成没有任何指针指向的地址因此造成内存泄漏
|
|
|
Re: 如果理解这个关于指针参数传递内存的论断
|
|
1)副本,其实就是将外面的变量值压栈。看来我主要是对流行的术语掌握得还不够,初学者,请见谅,呵呵。
2)其实他讲p就好了,没必要再弄个—P出来,函数将str的值压栈,再赋给p,简洁明了。--呵呵,是不是有点大不敬啊,那篇东西可是个牛人写的。
谢两位了
|
|
|
Re: 如果理解这个关于指针参数传递内存的论断
|
|
我的看法是:所有的参数传递只有一种,那就是值传递,根本就没有传地址这个概念。
举例如下:
把 char * p 看成 char *类型的变量, 或者干脆 typedef char * NEW_TYPE, 然后把 char *p 改写成 NEW_TYPE p.
这样的话,函数func(char *p) 就变成func(NEW_TYPE p),看起来象不象值传递啊?然后就可以按照简单的值传递的方法来分析一下这个变量p的用法了。
|
|
|
Re: 如果理解这个关于指针参数传递内存的论断
|
|
当然传地址也是传一个值,但是对于一个指针变量来说,传值是指传它的内容,传址是传这个指针所在的地址,还是有所区分为好(然后在被调用函数中修改这个地址所在的内容.如你例子中的做法好像有些不妥.
|
|
|
Re: 如果理解这个关于指针参数传递内存的论断
|
|
都有道理。
当初课本上分成值传递和地址传递的原因,我看估计是受了汇编语言的影响。
当初,在学指针的时候,被两者之间的区别搞得够呛。
从目前的趋势来看,指针的概念不是太明显了。
当初C语言的规范也够讨厌的。象:
char *p1, p2;
p1是指针,p2不是。造成了多少的bug。
如果从开始,就把char *看成一种类型,该多好。
|
|
|
Re: 如果理解这个关于指针参数传递内存的论断
|
|
>>如果从开始,就把char *看成一种类型,该多好。
"很"有道理! :-)
|