在C中通过引用传递


204

如果C不支持通过引用传递变量,为什么这样做有效?

#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
i = 21 

22
您在代码中的哪里传递引用
Atul 2015年

15
应该注意的是,C没有通过引用传递,只能使用指针进行模拟
某些程序员花了

6
正确的说法是“ C不支持通过引用隐式传递变量”-您需要&在调用函数之前显式创建一个引用(使用),并在函数中显式取消引用(使用*)。
克里斯·多德

2
当调用f(&i);这是按引用传递的实现时,您的代码输出完全相等,这不完全存在于C中。C按引用传递
EsmaeelE

@Someprogrammerdude传递指针是按引用传递。这似乎是“精明” C程序员引以为豪的事实之一。就像他们被踢出去一样。“哦,您可能认为C具有传递引用,但是不,实际上它只是harharhar传递的内存地址的值”。从字面上传递引用只是意味着传递变量存储位置的内存地址,而不是传递变量值本身。这就是C所允许的,并且每次传递指针时它都是按引用传递的,因为指针是对变量存储位置的引用
YungGun

Answers:


315

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


4
f(p); ->这是否意味着按价值传递?然后解引用它以获得指向的整数。->请您提供更多解释。
bapi 2014年

4
@bapi,取消引用指针的意思是“获取此指针引用的值”。
Rauni Lillemets 2014年

我们所称呼的是调用函数的方法,该方法采用变量的地址而不是传递指针。示例:func1(int&a)。这不是参考电话吗?在这种情况下,实际上是引用,而在使用指针的情况下,我们仍然按值传递,因为我们仅按值传递指针。
乔恩·惠洛克

1
使用指针时,关键事实是将指针的副本传递到函数中。然后该函数使用该指针,而不是原始指针。这仍然是按值传递,但是可以。
Danijel

1
@Danijel可以将不是任何副本的指针传递给函数调用。例如,调用该函数funcfunc(&A);这将向该函数传递一个指向A的指针,而不复制任何内容。它是按值传递的,但是该值是一个引用,因此您要“按引用传递”变量A。不需要复制。可以说它是按引用传递的。
YungGun

124

那不是引用传递,而是其他人所说的值传递。

C语言毫无例外地传递值。将指针作为参数传递并不意味着传递引用。

规则如下:

函数无法更改实际参数值。


让我们尝试看看函数的标量和指针参数之间的差异。

标量变量

这个简短的程序显示了使用标量变量的值传递。param被称为形式参数,variable在函数调用时称为实际参数。注意param,该功能的增加不会改变variable

#include <stdio.h>

void function(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

这使您相信该参数是通过引用传递的。它不是。它通过值传递,参数值是一个地址。int类型的值增加了,这就是使我们认为这是一个传递引用函数调用的副作用。

指针-按值传递

我们如何证明/证明这一事实?好吧,也许我们可以尝试第一个标量变量示例,但是我们使用地址(指针)代替标量。让我们看看是否有帮助。

#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

在我看来,这清楚地证明了指针是按值传递的。否则ptrNULL在函数调用之后。



50

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

void func(int& ref) {ref = 4;}

...
int a;
func(a);
// a is 4 now

1
我喜欢传递地址的答案。更有道理。
Afzaal Ahmad Zeeshan

在此上下文中,地址和引用是同义词。但是您可以使用这些术语来区分这两个术语,只是对它们的原始含义不诚实。
YungGun

27

您的示例之所以有效,是因为您将变量的地址传递给了一个函数,该函数使用解引用运算符来操纵其值

虽然C不支持参考数据类型,但是仍然可以通过显式传递指针值来模拟按引用传递,如您的示例。

C ++引用数据类型的功能不强,但被认为比从C继承的指针类型更安全。这就是您的示例,适合使用C ++引用

void f(int &j) {
  j++;
}

int main() {
  int i = 20;
  f(i);
  printf("i = %d\n", i);

  return 0;
}

3
该Wikipedia文章是关于C ++的,而不是关于C的。引用在C ++之前存在,并且不依赖于特殊的C ++语法存在。

1
@Roger:好点……我从答案中删除了对C ++的明确引用。
丹尼尔·瓦萨洛

1
那篇新文章说“引用通常被称为指针”,这与您的回答并不完全相同。

12

通过value传递了一个指针(地址位置)。

就像说“我要您更新的数据在这里”。


6

p是一个指针变量。它的值是i的地址。调用f时,将传递 p 的值,即i的地址。



5

在C语言中,一切都是传递值。指针的使用给我们一种幻觉,即我们正在通过引用传递变量,因为变量的会发生变化。但是,如果要打印出指针变量的地址,则会发现它不会受到影响。一个副本的的价值地址被传入到函数。以下是说明这一点的代码段。

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

4

因为您正在将指向变量p的指针(内存地址)传递给函数f。换句话说,您传递的是指针而不是引用。


4

简短的答案:是的,C确实通过使用指针的引用来实现参数传递。

在实现参数传递时,编程语言的设计人员使用三种不同的策略(或语义模型):将数据传输到子程序,从子程序接收数据,或同时执行这两种方法。这些模型通常分别称为in模式,out模式和inout模式。

语言设计师已经设计了几种模型来实现这三种基本参数传递策略:

值传递(输入模式语义)值传递(输出模式语义)值传递结果(输入模式语义)引用传递(输入模式语义)姓名传递(输入模式)语义)

引用传递是用于inout模式参数传递的第二种技术。运行时系统不是在主例程和子程序之间来回复制数据,而是向子程序的数据发送直接访问路径。在这种策略中,子程序可以直接访问数据,并与主例程有效地共享数据。此技术的主要优点是,它在时间和空间上绝对有效,因为不需要复制空间,也不需要数据复制操作。

C中的参数传递实现:C使用指针作为参数来实现值传递和引用传递(输入输出模式)语义。指针被发送到子程序,并且根本不复制任何实际数据。但是,由于指针是对主例程数据的访问路径,因此子程序可能会更改主例程中的数据。C采用了ALGOL68的这种方法。

C ++中的参数传递实现:C ++还使用指针以及一种称为参考类型的特殊类型的指针来实现按引用传递(输入模式)语义。引用类型指针在子程序中隐式取消引用,但它们的语义也通过引用传递。

因此,这里的关键概念是按引用传递实现对数据的访问路径,而不是将数据复制到子程序中。数据访问路径可以是显式解除引用的指针,也可以是自动解除引用的指针(引用类型)。

有关更多信息,请参阅Robert Sebesta所著的《编程语言的概念》,第10版,第9章。


3

您不是通过引用传递int,而是通过值传递指向int的指针。语法不同,含义相同。


1
+1“语法不同,含义相同。” ..同样,含义比语法更重要。

不对。通过调用void func(int* ptr){ *ptr=111; int newValue=500; ptr = &newvalue }int main(){ int value=0; func(&value); printf("%i\n",value); return 0; },它打印111,而不是500。如果你是通过引用传递,它应打印500 C不支持通过引用传递参数。
Konfle Dolex

@Konfle,如果您在语法上通过引用传递,ptr = &newvalue则将被禁止。不管有什么区别,我认为您指出的是“相同含义”并不完全正确,因为您在C语言中还具有其他功能(能够重新分配“引用”本身)。
xan 2013年

我们绝不会编写类似ptr=&newvalue通过引用传递的内容。取而代之的是,我们写ptr=newvalue这是C ++中的一个示例:void func(int& ptr){ ptr=111; int newValue=500; ptr = newValue; }传递给func()的参数的值将变为500
Konfle Dolex

就我上面的评论而言,通过引用传递参数毫无意义。但是,如果参数是对象而不是POD,则这将产生很大的不同,因为param = new Class()在函数内部进行的任何更改(如果通过值(指针)传递)都不会对调用方产生影响。如果param通过引用传递,则更改对于呼叫者将是可见的。
Konfle Dolex

3

在C语言中,要使用引用传递引用,请使用&应该对变量使用的address-of运算符,但在这种情况下,由于使用了指针变量p,因此无需在其前缀上使用address-of运算符。如果您用作&i参数,那将是正确的f(&i)

您也可以添加它,以解除引用p并查看该值如何匹配i

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

您为什么觉得需要重复所有代码(包括该注释块..)以告诉他他应该添加一个printf?

@Neil:该错误是@William的编辑引入的,我现在将其扭转。现在,显然tommieb几乎是正确的:您可以将&应用于任何对象,而不仅仅是变量。

2

指针和引用是两个不同的想法。

我未曾提及的几件事。

指针是某物的地址。可以像存储任何其他变量一样存储和复制指针。因此,它具有尺寸。

参考应被视为事物的别名。它没有大小,无法存储。它必须引用一些东西,即。它不能为null或更改。好吧,有时编译器需要将引用存储为指针,但这是实现细节。

使用引用,您就不会遇到指针问题,例如所有权处理,空值检查,使用时取消引用。


1

“通过引用传递”(通过使用指针)从一开始就在C中。您为什么认为不是呢?


7
因为从技术上讲,它不是通过引用传递的。
Mehrdad Afshari'2

7
传递指针值与传递引用不同。更新的值j *j)在f()具有不影响imain()
约翰·波德

6
语义上讲,通过引用传递相同,并且足以说它通过引用传递。的确,C标准没有使用“引用”一词,但这既不令我感到惊讶,也不是问题。即使我们可能提到标准,我们也不会说SO标准语言,否则我们不会看到有人在谈论rvalues(C标准不使用该术语)。

4
@吉姆:感谢您告诉我们,您对约翰的评论持反对意见。

1

我认为C实际上支持引用传递。

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

C为此也需要语法糖。参数类型声明中的*和参数中的&。因此*和&用于通过引用传递的C语法。

现在,人们可能会争辩说,真正的按引用传递应该只需要在参数声明中使用语法,而不需要在参数方面。

但是现在出现了C#,它确实支持引用传递,并且在参数和参数方面需要语法糖。

C没有by-ref传递的论点导致语法元素表示它表现出底层的技术实现根本不是论点,因为这或多或少地适用于所有实现。

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

这显然是一个危险的论点,因为语言中的许多其他功能是由其他功能组成的。(例如C ++中的字符串支持)


1

您正在做的是按值传递而不是按引用传递。因为您要将变量“ p”的值发送到函数“ f”(主要是f(p);)

C中带有按引用传递的相同程序看起来像(!!!该程序给出2个错误,因为C不支持按引用传递)

#include <stdio.h>

void f(int &j) {    //j is reference variable to i same as int &j = i
  j++;
}

int main() {
  int i = 20;
  f(i);
  printf("i = %d\n", i);

  return 0;
}

输出:-

3:12:错误:在'&'标记之前应有';',','或')'
             无效f(int&j);
                        ^
9:3:警告:函数'f'的隐式声明
               F A);
               ^
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.