本文目录
本文是自己对 C 语言参数传递的理解,实际上 C 语言只有值传递一种参数传递方式。
⚠️ 注意:本文写于 2021 年,内容较为基础。
回顾
在初学 C 语言时,从教材或者课堂上会学到 C 语言有两种参数传递方式,一种是值传递,另一种是地址传递。
- 值传递只向函数中传递实参的值,形参是实参的拷贝,在函数中修改形参的值,并不会改变函数外实参的值。
- 通常所说的“地址传递”是把对象的地址作为一个指针值传入函数。指针形参本身仍是副本;当它确实指向调用方的对象时,通过解引用可以修改那个对象。
例如要想交换 a, b 两个变量的值,应该写为
void swap(int *a, int *b)
{
int t = *a;
*a = *b;
*b = t;
}
调用时需要写成 swap(&a, &b)。函数收到的是两个地址值的副本,但通过 *a、*b 访问的仍是调用方的两个对象,所以对象中的值会被调换。
问题发现
当我在写编译原理实验时,我想要通过递归遍历语法树,生成中间代码的链表,并每次插入在链表头部,这就需要在函数间传递一个指向链表头的指针。但我发现我并不能在调用函数后获取到处理后的链表头,好像并没有进入递归函数一样。
问题分析
指针在 C 语言中也是一个对象,它保存的是地址。把指针作为实参传入函数时,形参得到的是该地址值的副本。函数可以通过这个副本修改所指对象,却不能直接改写调用方那个指针对象本身。
因此 C 语言的参数传递机制只有值传递。“地址传递”是常见的教学简称,并不是第二种语言级传参机制;这里按值传递的内容恰好是一个地址。
那么如果想改变指针自身的值应该如何传参呢?
其实前面已经给出了答案,既然指针也是变量,那么要想改变函数外变量的值,只需要声明一个指向它的指针,然后向函数中传递这个指针的拷贝就可以了。所以要改变指针的值,需要传递指针的指针,即二级指针。
例如可以声明
void analyse(InterCode **node);
在使用时按如下方式调用:
InterCode* head = NULL;
analyse(&head);
如果真实函数还需要其他参数,可以把它们放在 node 之前。原先用来表示“其他参数”的 void analyse(..., InterCode **node) 不能作为 C 代码:省略号表示可变参数时只能出现在参数列表末尾。
函数在接收到 InterCode ** 后,可以通过 *node = new_head 改变调用方 head 保存的地址。这里仍然是值传递,只是传递的值恰好是 head 自身的地址。
这与 ISO C11 委员会草案 N1570 的 6.5.2.2一致:调用前先求值各实参,再把相应实参的值赋给各形参;修改形参不会反向修改实参,但可以通过传入的指针修改其所指对象。
觉得有帮助?
分享给同样关注系统性能的人。