指针是不是被认为是 C 中通过引用调用的方法?

Posted

技术标签:

【中文标题】指针是不是被认为是 C 中通过引用调用的方法?【英文标题】:Are pointers considered a method of calling by reference in C?指针是否被认为是 C 中通过引用调用的方法? 【发布时间】:2016-02-10 21:43:42 【问题描述】:

在我大学的 C 编程课上,教授和她写的后续书籍在提到 C 中的指针时使用了call or pass by reference这个术语。

我的教授认为是“引用函数调用”的示例:

int sum(int *a, int *b);

我的教授认为是“按值函数调用”的示例:

int sum(int a, int b);

我 read C 不支持引用调用。据我了解,指针按值传递

基本上,说指针是 C 的引用传递方式是否不正确?说您不能在 C 中通过引用传递但可以使用指针作为替代方案会更正确吗?


2015 年 11 月 11 日更新

从我提出问题的方式来看,我相信术语的争论已经开始,事实上我看到了两个具体的区别。

pass-by-reference(目前主要使用的术语):在 C++ 等语言中使用的特定术语 pass-by-reference(我的教授用作解释指针范式的术语):在开发 C++ 等语言之前使用的通用术语,因此在重写该术语之前

阅读@Haris 的更新答案后,为什么这不是那么黑白分明是有道理的。

【问题讨论】:

“说你不能在 C 中通过引用传递,但可以使用指针作为替代,这样会更正确吗?” 作为一个不写 C 但谁经常解释 pass-by-valuepass-by-reference 之间的区别:是的。我发现关于这个主题的***文章非常好:en.wikipedia.org/wiki/Evaluation_strategy。 C 是按值传递。就 C 的范例而言,通过引用传递并没有真正的意义。 我的猜测是你的教授在没有太多语言的时候学习了 C,并且对 按引用传递 的确切含义进行挑剔不是问题。想一想:您计算机的 CPU 不支持“按引用传递”,这是为了让人类更容易而发明的。 @Panzercrisis “...不是对正在发生的事情的字面描述。” - 我认为情况正好相反:创建一个指向某个变量的指针,然后传递该指针是 exactly 最终通过引用调用所做的。因此,为了说明值和引用之间的区别,C 示例运行良好。您必须自己处理引用和取消引用,因为语言 C 是按值调用的,其中 C++ 在编译器中向您隐藏了那部分样板代码。 @Insane “从以前学习 Java(没有引用调用)” ...当您在函数中改变传递的对象时会发生什么? 【参考方案1】:

您不能在 C 中通过引用传递,但可以使用指针作为替代方式

是的,没错。


再详细一点。无论你作为参数传递给 c 函数,它都只通过值传递。不管是变量的值还是变量的地址。

不同之处在于您发送的内容。

当我们通过值传递时,我们将变量的值传递给函数。当我们通过引用传递时,我们将变量的别名传递给函数。 C可以将指针传递给函数,但这仍然是按值传递。它正在将指针的值、地址复制到函数中。


如果您要发送变量的值,那么函数只会接收该值,并且更改该值不会影响原始值。

如果您要发送变量的地址,那么也只会发送值(在这种情况下为地址),但由于您拥有变量的地址,因此可以使用它来更改原始值。


作为示例,我们可以查看一些 C++ 代码来了解按值调用和按引用调用之间的真正区别。取自this 网站。

// Program to sort two numbers using call by reference. 
// Smallest number is output first.

#include <iostream>
using namespace std;

// Function prototype for call by reference
void swap(float &x, float &y);

int main()

   float a, b;

   cout << "Enter 2 numbers: " << endl;
   cin >> a >> b;
   if(a>b) 
     swap(a,b); // This looks just like a call-by-value, but in fact
                // it's a call by reference (because of the "&" in the
                // function prototype

   // Variable a contains value of smallest number
   cout << "Sorted numbers: ";
   cout << a << " " << b << endl;
   return 0;


// A function definition for call by reference
// The variables x and y will have their values changed.

void swap(float &x, float &y)
// Swaps x and y data of calling function

   float temp;

   temp = x;
   x = y;
   y = temp;

在这个 C++ 示例中,引用变量(在 C 中不存在)被使用。引用this网站,

“引用是一个别名,或者是现有变量的替代名称...”,

“引用的主要用途是作为函数形式参数来支持传递引用...”

这与使用指针作为函数参数不同,因为,

"指针变量(或简称指针)与其他变量基本相同,可以存储一条数据。与普通变量存储值不同(如int、double、a char),一个指针存储一个内存地址。”

所以,本质上,当一个发送地址并通过指针接收时,一个只发送值,但当一个发送/接收引用变量时,一个发送别名或引用。


**更新:2015 年 11 月 11 日**

在C Chatroom上一直争论不休,看了cmets和这个问题的答案后,我意识到可以换一种方式来看待这个问题,换个角度也就是。

让我们看一些简单的 C 代码

int i;
int *p = &i;
*p = 123;

在这种情况下,可以使用以下术语:p 的值是对 i 的引用。因此,如果是这种情况,那么如果我们将相同的指针 (int* p) 发送给一个函数,人们可能会争辩说,因为 i 的引用被发送给了该函数,因此这可以称为 按引用传递

所以,这是一个术语和看待场景的方式的问题。

我不会完全不同意这个论点。但是对于一个完全遵守书本和规则的人来说,这是错误的。


注意:更新灵感来自this聊天。

【讨论】:

在这种情况下,我会说使用指针来解释传递引用是非常误导性的,也从不明确地说指针仍然是 C 中的值传递。 @Insane,是的。我见过大学教授这样解释。一开始我也被误导了,后来我明白了。 不仅有些教授,有些书也把它当作参考传递。 @Namfuak,以错误的方式解释一个术语,遗漏了细节,以使其更简单,对我来说听起来比复杂的解释更危险。 @Insane 我不认为它具有误导性,它只是在不同的上下文中使用——“引用”现在的含义有点不同,对于 C++ 和 C# 等语言。在更一般的术语中,指针对值的引用。他们只是没有我们现在与参考相关的其他属性。由于您的老师所教授的课程明确涉及 C(而不是 C++),所以这很好。您基本上是在抱怨使用“计算机”一词来指代计算东西的人-嗯,这就是它的意思!当然,今天你首先想象的是一台机器,但这只是你的背景。【参考方案2】:

引用在这里是一个重载的术语;一般来说,引用只是引用某物的一种方式。指针是指指向的对象,而(通过值)传递一个指向对象的指针是 C 中通过引用传递的标准方式。

C++ 引入引用类型作为表达引用的更好方式,并在技术英语中引入了歧义,因为我们现在可以使用术语“按引用传递”来指代使用引用类型通过引用传递对象。

在 C++ 上下文中,IMO 已弃用前一种用法。但是,我相信前一种用法在其他没有歧义的上下文(例如纯 C)中很常见。

【讨论】:

显然指针是一个引用...只是不是一个 c++ &reference...但它指的是一些内存块 确实,我曾经很困惑知道 x86 异常并在学习 C++ 时试图弄清楚为什么我的 catch(...) 不处理 #DE 异常(除以零错误)。结果发现有一个类似的术语重载:C++ 异常与 CPU 异常完全无关。 在 C++ 世界中,reference 表示特定的语言特性,我发现最好使用 handle 作为通用术语——它可以是引用、指针、数组索引、映射键、文件描述符等。并且传递句柄可能会导致句柄重复,但目标对象是共享的。【参考方案3】:

Does C even have ``pass by reference''?

不是。

严格来说,C 总是使用传值。您可以自己模拟通过引用传递,通过定义接受指针的函数,然后在调用时使用&amp; 运算符,当您将数组传递给函数时,编译器实际上会为您模拟它(通过传递指针,请参阅问题 6.4 等)。

另一种看待它的方式是如果参数具有类型,例如int *,则整数通过引用传递,而指向整数的指针通过值传递

从根本上说,C 没有什么真正等同于通过引用或c++ 引用参数的正式传递。

为了演示指针是按值传递的,让我们考虑一个使用指针进行数字交换的示例。

int main(void)

    int num1 = 5;
    int num2 = 10;

    int *pnum1 = &num1;
    int *pnum2 = &num2;
    int ptemp;

    printf("Before swap, *Pnum1 = %d and *pnum2 = %d\n", *pnum1, *pnum2);
    temp = pnum1;
    pnum1 = pnum2;
    pnum2 = ptemp;

    printf("After swap, *Pnum1 = %d and *pnum2 = %d\n", *pnum1, *pnum2);

不是交换数字,而是交换指针。现在为相同的功能制作一个函数

void swap(int *pnum1, int *pnum2)

     int *ptemp = pnum1;
     pnum1 = pnum2;
     pnum2 = temp;


int main(void)

    int num1 = 5;
    int num2 = 10;

    int *pnum1 = &num1;
    int *pnum2 = &num2;

    printf("Before swap, *pnum1 = %d and *pnum2 = %d\n", *pnum1, *pnum2);
    swap(pnum1, pnum2);

    printf("After swap, *pnum1 = %d and *pnum2 = %d\n", *pnum1, *pnum2);

轰隆隆!不换!


一些tutorials 提到指针引用作为引用调用,这是误导性的。有关difference between passing by reference and passing by value,请参阅this answer。

【讨论】:

+1;虽然我仍然喜欢@Haris 的答案,因为它感觉更完整,但即使在谷歌上搜索了很多之后,我也没有看到这个链接! "如果一个参数的类型是int *,那么一个整数是通过引用传递的,而一个指向整数的指针是通过值传递的。" -- 我认为这很关键。 @CompuChip,整数不是通过引用传递的,传递的地址被用来访问整数,因为地址是指整数。 @Haris 我不是在谈论 C++ 意义上的引用,而是更笼统的说法。对我来说,概念上int&amp;int* 在这个讨论中是同一回事。我刚刚阅读了 11 月 11 日对 OP 和您出色答案的更新,我认为术语上的区别在这里令人困惑。假设该观点已在聊天中广泛讨论,我将不再继续讨论,但我觉得这里没有根本的误解。 @hacks 再说一遍,我们似乎都同意 :)【参考方案4】:

来自 C99 标准(重点是我的):

6.2.5 类型

20 可以从对象和函数类型构造任意数量的派生类型,如 如下:

...

——指针类型e 可以从函数类型或对象类型派生,称为引用类型指针类型描述了一个对象,其值提供了对所引用类型实体的引用。从引用类型 T 派生的指针类型有时称为“指向 T 的指针”。从引用类型构造指针类型称为“指针类型派生”。指针类型是完整的对象类型。

基于上述,您的教授所说的有道理,是正确的。 指针按值传递给函数。如果指针指向一个有效的实体,它的值提供一个实体的引用

【讨论】:

查看我添加的示例,因为没有明确发送地址。这将是正确传递引用的一个示例。如果您显式发送地址并且它是由指针接收的,则它实际上只是按值发送。 嗯,是的,但我们又回到了 C、C++ 的苹果和橘子……例如,C 中没有 void swap(float &amp;x, float &amp;y),只有 void swap(float *x, float *y) @DavidC.Rankin 在语义上它们是相同的。执行后xy 的值被交换。语法不同,但意思完全一样。【参考方案5】:

“通过引用传递”是一个概念。是的,您将指针的值传递给函数,但在该实例中,该指针的值被用于引用变量。

有人用螺丝刀类比来解释说指针的传递称为引用传递是错误的,说你可以用硬币拧螺丝,但这并不意味着你会称硬币为螺丝刀。我会说这是一个很好的类比,但他们得出了错误的结论。事实上,虽然你不会声称硬币是螺丝刀,但你仍然会说你用它拧了螺丝。即,即使指针与 c++ 引用不同,您使用它们执行的操作 IS 通过引用传递。

【讨论】:

【参考方案6】:

C 按值、句点传递参数。然而,指针是一种机制,可用于有效地通过引用传递参数。就像硬币可以有效地用作螺丝刀,如果你有合适的螺丝:一些螺丝甚至被选择用来很好地操作硬币。他们仍然没有把硬币变成真正的螺丝刀。

C++ 仍然按值传递参数。 C++ 引用比指针更受限制(尽管有更多的隐式转换)并且不能成为数据结构的一部分,它们的使用看起来更像是通常的引用调用代码,但它们的语义虽然非常适合为了满足引用调用参数的需求,仍然比纯引用调用实现(如 Fortran 参数或 Pascal var 参数)更切实,并且您可以在函数调用上下文之外完美地使用引用。

【讨论】:

【参考方案7】:

你的教授是对的。 按 value ,它被复制。 通过引用,它不是复制的,引用说明它在哪里。

通过值,你将一个 int 传递给一个函数,它被复制,对副本的更改不会影响原始。

通过引用,传递与指针相同的int,它不是复制的,你是在修改原来的。

通过引用,数组总是通过引用,您的数组中可能有十亿个项目,直接说出它在哪里会更快,您正在修改原始数据。

【讨论】:

【参考方案8】:

在支持按引用传递的语言中,有一种方法可以为函数提供一些可用于标识调用者知道的变量的方法,直到被调用的函数返回,但它只能存储在之后将不存在的地方。因此,调用者可以知道,由于向某个函数传递对变量的引用而对变量执行的任何操作都将在函数返回时完成。

比较 C 和 C# 程序:

// C               // C#
int x=0;           int x=0;
foo(&x);           foo(ref x);
x++;               x++;
bar();             bar();
x++;               x++;
boz(x);            boz(x);

C 编译器无法知道“bar”是否会改变 x,因为 foo() 收到一个不受限制的指向它的指针。相比之下,C# 编译器 知道 bar() 不可能改变 x,因为 foo() 只接收一个 对它和那里的临时引用(在 .NET 术语中称为“byref”) 该 byref 的任何副本都无法在 foo() 返回。

传递指向事物的指针允许代码与传递引用语义相同的事情,但传递引用语义使代码可以提供更强的保证:它不会做的事情。

【讨论】:

以上是关于指针是不是被认为是 C 中通过引用调用的方法?的主要内容,如果未能解决你的问题,请参考以下文章

在python中通过引用传递引用

有人可以解释在 C++ 中通过指针传递和通过引用传递的目的是啥吗?

在 C# 中通过引用传递对象和对象列表

在 C++ 中通过指针捕获异常

在函数c ++中通过引用传递的指针参数

将指针传递给函数不是在 C 中通过引用传递吗?