关于地址作为形参的问题..

把地址作为形参进行传递时..函数可以修改形参..从而修改实参..(比如数组传递)
但是最近自己在写链表时..发现以下问题..当我用head作为形参传递至deletef函数(删除节点)时..不能修改head的地址..(困惑)..代码如下:
void deletef(struct person *head)//单链.在main函数中建立..首地址head传入函数修改
{
struct person *p,*q;
int n;
p=q=head;
scanf("%d",&n);
while ((q!=NULL)&&((*q).num!=n))
{
p=q;//进行扫描
q=(*q).next;
}
if (q==NULL) printf("No Change\n");
else
{
if (q==head) head=(*q).next;//这个地方测试不能修改head的地址..
else if ((*q).next==NULL) (*p).next=NULL;
else (*p).next=(*q).next;
}
}
以上通过输出测试错误如下:
比如链表已经为1 2 3 4 5 6 NULL
然后输入1..无法删除1..仍然显示1 2 3 4 5 6 NULL
但是其他数据正常..(比如输入2,3,4,5,6)
不知道是什么原因?我可以通过返回地址指针解决这个问题..但是不知道产生这个问题原因..求高人解答..谢谢..

呵呵, 应该是你对传值和传地址的理解有误了吧

我的理解是这样的, 说出来, 如果有错误还请至改正
我们平时所说的传值传地址是因为形参代表的是用户的有效值还是有效值的地址,
比如我们如果传入的是普通变量的话是有效值的传输, 而指针或者普通变量的&操作的话就是传入的地址。

我们说当然我们说的传址能改变数据是因为我们对这个地址上承载的数据进行改变
比如function1(int * a)函数中我们进行了*a = 12;这个操作实际是这样的
我们通过int * a传递值的时候其实a作为承载地址的变量, 传进来的是地址值,
然后函数中我们通过*a表示了这个地址的值, 并利用*a = 12;将这个值改变了
这个操作直接操作的是调用函数中变量的地址, 而不是值(因为如果是变量值的话是相当于复制的), “但是非常关键的一点是我们这里改变的只是a代表地址上承载的数值的改变, a承载的地址并没有发生改变, 这点非常非常重要”

这就不难理解你这里面说的了, 如果是第一个元素就输你要删除的时候, 你改变的是head(注意这个其实是你的实参被改变, 是局部变量,而且外部的所谓的那个head的值没有改变, 因为可以假想head是被复制的)这就造成了head取值的时候取到的还是第一个值, 而后面的因为中间的连接关系(->next)发生了改变(注意这个操作是地址改变的), 当你通过head去访问的是后内部链接关系是被实实在在的改变的, 所以就出现了可以被正确实现的现象。

形象点就是:
老师给你一个纸条写着一个地址让你去A地(A地也有数据), A地记录有B地的地址和B的数据, 以此类推。注意老师给你的一个纸条其实老师那边自己保存的还是A地的地址(因为老师给你的其实还是副本), 然后你去把自己手上的A地址往后延的话, 老师那边访问的时候其实还是直接去找A地的, 但是你如果修改的是别的地方的话, 老师因为到A之后的地方也是通过前面的地点获取地址的, 所以是得到的你修改的地址。 这应该不难理解了吧?

总结下就是传参的是后其实都是想像成作为副本来传递的, 只不过这个副本有可能是个访问地址, 也有可能是个用户有效值的复制。 前者由于是使用的调用函数变量的地址,我们通过对该地址的值直接修改调用函数的变量的值, 后者因为是副本修改的时候调用函数的值不同步修改, 所以造成无效修改。

要改进你的实现的话有两种方式:
可以采用带头节点的head传入, 或者传入的时候利用struct person **head
建议使用第一种, 因为第二种的话操作起来复杂些而且要特别小心, 否则极易出现错误

上面是自己的一点理解, 希望对有你帮助
温馨提示:答案为网友推荐,仅供参考
第1个回答  2012-09-11
看了楼主的问题..和楼上的回答..我想说稍微有点错误..
楼主知道你错误在哪里了吗?
首先请你先明白形参和实参的区别..(前者在函数结束后会自动释放)
楼主传入一个head地址..很明显..deletef函数可以对head地址的内容进行修改..
先除去这条指令if (q==head) head=(*q).next;//这个地方测试不能修改head的地址..
你会发现其余代码全是在处理head的内容..这就是为什么其他数据正常..
当你加上
if (q==head) head=(*q).next;//这个地方测试不能修改head的地址..

这句时..问题来了..这里的head是形参..函数结束后会自动释放..也就是即使你修改head成功..也不能影响到实参的head..这就是为什么1不能删除...(楼主可以在deletef函数里输出..便会发现数据全部正常)..
所以解决方法:
加上返回指针..让head形参无论修改与否都要返回..这样就可以帮助实参不缺值..
然后请记住p,q只是处理head地址的指针变量..他们同样可以修改head的内容..因为指向了同一内存位置..但是你却不能改成以下

if (q==head) q=(*q).next;//这个地方测试不能修改head的地址..

然后return q;
这是你会发现输出的时q之后的数据..因为deletef函数寻找时q的地址指向了要删除数据的地址..(q具有对head进行修改的能力..实际上修改了..)
那这样楼主能改成
if (q==head) q=(*q).next;//这个地方测试不能修改head的地址..

return head;吗?
答案是不行的..形参是独立的..整个过程..虽然p,q修改了head的内容..但是对于第一个地址..显然head完全没有变..head仍然指向原来的数据..这时结果会出现无法删除第一个的情况..

所以正确修改如下
if (q==head) head=(*q).next;//这个地方测试不能修改head的地址..

记住return head;这样才是完全正确的..
楼主..你失误在于不是对地址理解错误..而是在于对实参和形参理解失误了..
请记住:
形参在最后都会被释放掉..(即是传递的是地址..虽然地址可以修改内容..因为指向同一块内存)..所以..根据你的需要..在形参释放掉之前处理好返回数据..

数组就是地址传送..类似的..(只是数组不会出现修改首地址的情况..略有不同..)
相似回答