一则经典的问题:
//为了突出程序主要目的,忽略了malloc()分配是否成功检查与free()相关逻辑
//这是第一段代码
struct test {
int num;
};
void change_value(struct test *out)
{
struct test *in = (struct test *)malloc(sizeof(struct test));
in->num = 100;
out = in;
printf("in num:%d\n",out->num);
}
int main()
{
struct test *out = (struct test *)malloc(sizeof(struct test));
out->num = 0;
change_value(out);
printf("out num:%d\n",out->num);
return 0;
}
输出
in num:100 out num:0
为何out num:0,在change_value内部我们已经把out指向in。in->num=100,那么out->num应该也等于100才是,导致这种“潜意识”的原因是我们经常会看见change_value如下实现:
//这是第二段代码
void change_value(struct test *out)
{
out->num = 100;
//.....
}
输出
out num:100
这里我们在change_value内部改变了out->num,回到main后out->num也理所应当的被改变了。这个时候可能会觉得在第一段change_value代码中,struct test *in使了什么魔术,为了验证是不是如此,我们继续改写change_value:
//这是第三段代码
void change_value(struct test *out)
{
struct test *in = (struct test *)malloc(sizeof(struct test));
in->num = 100;
out->num = 90;
out = in;
out->num = 80;
printf("in num:%d\n",out->num);
}
输出
in num:80 out num:90
是不是感觉更加神奇的事情来了,在change_value内部out->num=80,在main里out->num=90。难道是out = in这一行有什么神奇的魔法?不是,其实归根到底还是形参的问题。
形参 – 全称为“形式参数”是在定义函数名和函数体的时候使用的参数,目的是用来接收调用该函数时传递的参数。形参变量只有在被调用时才分配内存单元,在调用结束时,即刻释放所分配的内存单元。因此,形参只在函数内部有效。函数调用结束返回主调用函数后则不能再使用该形参变量。
分析:
在void change_value(struct test *out),out作为一个地址形参传入,它仅仅只是一个“副本”。之所以在第二段代码中可以改变out->num的值,原因是在change_value内部out虽然作为副本,但是out->num偏移的地址仍然“有效的”(这里有效是指,副本out与原始out地址相同,所以他们偏移指向的内容也相同)。
一旦地址改变,例如第三段代码中out=in,那么out->num就偏移到新的(in的)地址内容了,这也解释了第三段代码输出in num:80,out num:90的原因。简单图示:
这样也解释了下面2段程序输出区别的原因
struct test {
int *pnum;
};
void change_value(struct test *out)
{
out->pnum = (int *)malloc(sizeof(int));
}
int main()
{
struct test *out = (struct test *)malloc(sizeof(struct test));
change_value(out);
*(out->pnum) = 1;
printf("out pnum:%d\n",*(out->pnum));
return 0;
}
输出
out pnum:1
void change_value(int *out)
{
out = (int *)malloc(sizeof(int));
}
int main()
{
int *out = NULL;
change_value(out);
*out = 1;
printf("out pnum:%d\n",*out);
return 0;
输出
Segmentation fault (core dumped)
那么如何“忽略副本”,改变实际的out地址值呢?
1.使用指针的指针,即二维指针
struct test {
int num;
};
void change_value(struct test **out)
{
struct test *in = (struct test *)malloc(sizeof(struct test));
in->num = 100;
(*out)->num = 90;
(*out) = in;
(*out)->num = 80;
printf("in num:%d\n",(*out)->num);
}
int main()
{
struct test *out = (struct test *)malloc(sizeof(struct test));
change_value(&out);
printf("out num:%d\n",out->num);
return 0;
}
输出
in num:80 out num:80
2.传引用
struct test {
int num;
};
void change_value(struct test *&out)
{
struct test *in = (struct test *)malloc(sizeof(struct test));
in->num = 100;
out->num = 90;
out = in;
out->num = 80;
printf("in num:%d\n",out->num);
}
int main()
{
struct test *out = (struct test *)malloc(sizeof(struct test));
change_value(out);
printf("out num:%d\n",out->num);
return 0;
输出
in num:80 out num:80
小结:这里没有什么奇怪的魔法:),只是需要仔细理解形参的意义-即传入指针的地址,这个地址偏移意味着什么,如果将地址副本改变偏移又意味什么。
(全文结束)
转载文章请注明出处:漫漫路 - lanindex.com
