在C中通过引用传递

如果C不支持通过引用传递一个variables,为什么这个工作?

#include <stdio.h> void f(int *j) { (*j)++; } int main() { int i = 20; int *p = &i; f(p); printf("i = %d\n", i); return 0; } 

产量

 $ gcc -std = c99 test.c
 $ a.exe
我= 21 

因为您将指针的值传递给该方法,然后将其解引用以获取指向的整数。

在C中,传递引用是通过传递一个variables(一个指针)的地址来模拟的,并且在函数中取消引用该地址来读取或写入实际的variables。 这将被称为“C型传递参考”。

资料来源:www-cs-students.stanford.edu

这不是传递参考,就像其他人所说的那样,传递价值。

C语言无一例外都是按值传递的。 传递指针作为参数并不意味着传递参考。

规则如下:

一个函数不能改变实际的参数值。


我们试着看看函数的标量和指针参数之间的区别。

标量variables

这个简短的程序显示了使用标量variables的传值。 param被称为forms参数,函数调用中的variable被称为实际参数。 注意在函数中增加param不会改变variable

 #include <stdio.h> void function2(int param) { printf("I've received value %d\n", param); param++; } int main(void) { int variable = 111; function(variable); printf("variable %d\m", variable); return 0; } 

结果是

 I've received value 111 variable=111 

通过引用的错觉

我们稍微改变一段代码。 param现在是一个指针。

 #include <stdio.h> void function2(int *param) { printf("I've received value %d\n", *param); (*param)++; } int main(void) { int variable = 111; function2(&variable); printf("variable %d\n", variable); return 0; } 

结果是

 I've received value 111 variable=112 

这使你相信参数是通过引用传递的。 不是。 它是按值传递的,参数值是一个地址。 inttypes的值增加了,这是使我们认为它是一个传递引用函数调用的副作用。

指针 – 传递值

我们如何显示/certificate这一事实? 那么,也许我们可以尝试标量variables的第一个例子,而不是标量,我们使用地址(指针)。 让我们看看是否可以帮助。

 #include <stdio.h> void function2(int *param) { printf("param's address %d\n", param); param = NULL; } int main(void) { int variable = 111; int *ptr = &variable; function2(ptr); printf("ptr's address %d\n", ptr); return 0; } 

结果将是这两个地址是平等的(不要担心确切的价值)。

示例结果:

 param's address -1846583468 ptr's address -1846583468 

在我看来,这清楚地certificate指针是按值传递的。 否则函数调用后ptr将为NULL

因为在上面的代码中没有传递引用。 使用指针(如void func(int* p) )是传递地址。 这是C ++中的传递引用(在C中不起作用):

 void func(int& ref) {ref = 4;} ... int a; func(a); // a is 4 now 

你的例子的工作原理是因为你将variables的地址传递给一个函数,该函数使用解引用操作符来操作它的值。

虽然C不支持引用数据types ,但仍然可以通过显式传递指针值来模拟传递引用,如示例中所示。

C ++引用数据typesfunction较弱,但比从Cinheritance的指针types更安全。这将是您的示例,适用于使用C ++引用 :

 void f(int &j) { j++; } int main() { int i = 20; f(i); printf("i = %d\n", i); return 0; } 

您正在通过传递一个指针 (地址位置)。

这就像是说“这里是我要你更新数据的地方”。

p是一个指针variables。 它的值是我的地址。 当你打电话给f时,你传递了 p 的值 ,这是我的地址。

C中没有引用引用,但是p引用了i,并且你传递了p的值。

在C中,一切都是按价值传递的。 指针的使用给了我们错觉,因为variables的发生变化,所以我们通过引用传递。 但是,如果要打印指针variables的地址,则会看到它不受影响。 地址的一个副本被传递给函数。 下面是一个说明这一点的片段。

 void add_number(int *a) { *a = *a + 2; } int main(int argc, char *argv[]) { int a = 2; printf("before pass by reference, a == %i\n", a); add_number(&a); printf("after pass by reference, a == %i\n", a); printf("before pass by reference, a == %p\n", &a); add_number(&a); printf("after pass by reference, a == %p\n", &a); } before pass by reference, a == 2 after pass by reference, a == 4 before pass by reference, a == 0x7fff5cf417ec after pass by reference, a == 0x7fff5cf417ec 

因为你将一个指针(内存地址)传递给variablesp到函数f中。 换句话说,你正在传递一个指针而不是引用。

你不是通过引用传递一个int,而是通过值传递一个指向int的指针。 不同的语法,相同的意思。

在C中,要通过引用传递你使用的地址 – 运算符&哪个应该用于variables,但在你的情况,因为你已经使用了指针variablesp ,你不需要前面加上运算符地址。 如果你使用&i作为参数,那就是真的了: f(&i)

你也可以添加这个,解引用p ,看看这个值如何匹配i

 printf("p=%d \n",*p); 

简单的回答:是的,C确实使用指针实现了通过引用传递的参数。

在实现parameter passing时,编程语言的devise者使用三种不同的策略(或语义模型):将数据传输到子程序,从子程序接收数据,或者同时执行两者。 这些模型通常在模式,输出模式和input模式中相应地被称为。

语言devise者已经devise了几个模型来实现这三个基本的parameter passing策略:

传递值(模式语义)传递结果(out模式语义)传递值结果(inout模式语义)传递引用(inout模式语义)传递名称(inout模式语义)

传递参考是第二种技术,用于inout模式parameter passing。 运行系统不是在主程序和子程序之间来回复制数据,而是直接访问子程序的数据。 在这个策略中,子程序可以直接访问与主程序有效共享数据的数据。 这种技术的主要优点是它的时间和空间效率绝对高,因为不需要复制空间,也没有数据复制操作。

C:C中的parameter passing实现使用指针作为参数来实现按值传递以及传递引用(inout模式)语义。 指针被发送到子程序,根本没有实际的数据被复制。 但是,因为指针是主程序数据的访问path,所以子程序可以改变主程序中的数据。 C从ALGOL68采用了这种方法。

C ++中的parameter passing实现:C ++还使用指针实现了传递引用(inout模式)语义,还使用了一种称为引用types的特殊types的指针。 引用types指针在子程序内部被隐式地解引用,但是它们的语义也是通过引用的。

所以这里的关键概念是传递引用实现了对数据的访问path,而不是将数据复制到子程序中。 数据访问path可以是明确解除引用的指针或自动解引用的指针(引用types)。

欲了解更多信息,请参阅Robert Sebesta,第10版,第9章的“程序devise语言概念”一书。

“通过引用传递”(通过使用指针)已经从C开始。 你为什么觉得不是?

我认为C实际上支持通过引用。

大多数语言要求语法糖通过引用而不是价值。 (例如C ++在参数声明中需要&)。

C也需要这个语法糖。 它在参数types声明中是*,在参数上是&。 所以*和& 通过引用传递的C语法。

现在可以争辩说,真正的按引用传递应该只需要参数声明的语法,而不是在参数方面。

但是现在来的是C#, 通过引用传递来支持, 并且在参数和参数两端需要语法糖。

C没有by-ref传递的论点导致语法元素expression它展现出底层的技术实现根本不是一个参数,因为这对于所有的实现都或多或less地适用。

唯一剩下的论点是,在C中传递ref不是一个单一的特征,而是结合了两个现有的特征。 (以&引用参数,希望引用types为*)。例如C#确实需要两个语法元素,但不能互相使用。

这显然是一个危险的说法,因为语言中的很多其他function都是由其他function组成的。 (如C ++中的string支持)