一则经典的问题:
//为了突出程序主要目的,忽略了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