如何在不实际将地址传递给函数的情况下实现“按引用传递”?

时间:2018-12-07 12:28:46

标签: language-agnostic pass-by-reference pass-by-value

我很清楚在C和C ++中所有东西都是通过值传递的(即使该值是引用 type )。我认为(但我不是专家)对于Java同样如此。

所以,这就是为什么我将与语言无关的标记包括在内的原因,我可以在不传递 some 值的情况下以哪种语言将任何内容传递给函数?

如果存在,该机制是什么样的?我对此进行了认真的思考,但我没有提出任何不涉及值传递的机制。

即使编译器以我在内存中没有指针/引用作为真正变量的方式进行优化,它仍然必须计算一个地址作为与堆栈(帧)指针的偏移量,然后传递该地址。

有人能启发我吗?

6 个答案:

答案 0 :(得分:3)

从C的角度来看:

没有参考作为语言级别的概念。通过使用指针指向对象来引用对象。

指针的值是指向对象的地址。像其他任何参数一样,指针也按值传递。指向对象在概念上通过引用传递。


至少从C ++角度来看:

  

如何通过[引用]实现[...]?

通常,通过复制对象的地址。

  

...实际上没有将地址传递给函数?

如果函数调用被内联扩展,则无需将地址复制到任何地方。指针也是如此,因为可能的规则可能会删除副本。


  

我可以用什么语言在不传递任何值的情况下将任何东西传递给函数?

这种语言的功能概念必须与C显着不同。必须没有堆栈框架推送。

类似于函数的C预处理器宏,顾名思义,类似于函数,但是它们的参数不会在运行时传递,因为预处理是在编译之前进行的。

另一方面,您可以具有全局变量。如果您更改程序的全局状态并调用不带参数的函数,则从概念上讲您是“将新的全局状态传递给了该函数”,而未传递任何值。

答案 1 :(得分:2)

指针是值。 Valueaar值。值具有唯一标识,需要存储。

引用不是值。参考没有身份。如果我们有:

int x=0;
int& y=x;
int& z=x;

yz都是对x的引用,它们没有独立的标识。

相比:

int x=0;
int* py=&x;
int* pz=&x;

pypz都是指向x的指针,它们具有独立的标识。您可以修改py而不是pz,可以得到它们的大小,可以记忆它们。

在某些情况下,在机器代码级别,引用的实现方式与指针相同,不同之处在于,从不对某些操作执行某些操作(例如重新调用它们)。

但是C ++并不是根据机器代码定义的。它是定义抽象机器行为的术语。编译器将您的代码编译为在此抽象机上的操作,该抽象机没有固定的调用约定(按标准),没有引用的布局,没有堆栈,没有堆等。然后对其进行任意转换,而不会更改as-如果有行为(常见的行为是单项分配),则重新安排事情,然后在某个时刻发出汇编/机器代码,这些代码会在您所运行的实际硬件上生成类似的行为。

现在,编译C ++的近乎通用的方法是编译单元/链接器模型,该函数将功能导出为符号,并提供了固定的ABI调用约定,供其他编译单元使用。然后在链接阶段将编译单元连接在一起。

在那些ABI中,引用作为指针传递。

答案 2 :(得分:1)

如何在不实际将地址传递给函数的情况下实现“按引用传递”?

在C语言的上下文中,简短的答案是:

  • 在C中,不是。
  • 在C ++中,后跟与号(&)的类型为 reference type 。 例如,int&是对int的引用。传递参数时 到具有引用类型的函数时,该对象才真正传递 引用。 (有关更多信息,请参见下面的 scholarly 链接。)

但实际上,大多数混淆是语义。某些困惑可以通过以下方式得到解决:

  • 1)停止使用模拟一词来描述传递地址
  • 2)停止使用 reference 一词来描述地址

  • 3)认识到在C / C ++语言的上下文中, 短语通过引用,单词reference定义为:值 地址

除此之外,还创建了许多 illusions concepts 来传达 impossible ideas 的示例>。不管有多少篇学术论文或实际讨论,非仿效的通过引用的概念可以说是其中之一。

This one (学术论文类别)是另一个在模拟实际通过引用之间进行区分的方法在同时使用C和C ++的讨论中,但谁的结论与现实紧密相关。以下是摘录:

  

...以某种方式,这仅仅是编程语言实际上如何实现“按引用传递”的概念的问题:C通过使用指针并将其按值传递给函数来实现这一点,而C ++提供了两种实现。从一方面讲,它重用了从C派生的相同机制(即指针+按值传递)。另一方面,C ++还提供了本机的“按引用传递”解决方案,该解决方案利用了引用类型的思想。因此,即使在C ++中,即使您要传递一个àC指针,也并不是真正通过引用传递,而是通过值传递指针(当然,除非您将引用传递给指针!例如,int *&)。   由于“按引用传递”一词可能存在歧义,因此最好在使用引用类型时仅在C ++上下文中使用它。

但是,正如您和其他人已经指出的那样,在通过参数传递任何内容的概念中,无论是 value 还是 reference , >根据定义必须具有 value

答案 3 :(得分:1)

在机器代码级别,“按引用传递X”本质上就是“按值传递X的地址”。

答案 4 :(得分:0)

按值传递的意思是对象自身被传递。

指针传递中,我们将指针的值传递给对象。

引用传递中,我们以相同的方式传递引用(基本上是我们知道的指向对象的指针)。

是的,我们总是传递一个值,但是问题是值是什么?对象本身并不总是如此。但是,当我们说用**传递变量时,我们给出的信息与我们要传递的对象有关,而不是实际传递的值。

答案 5 :(得分:-1)

我相信这个问题是基于误解。我没有足够的知识来评论语言的内部原理,但是我可以通过解决这一点来使事情变得简单一些:

  

我很清楚,在C和C ++中,所有内容都是按值传递的(即使该值是引用类型)

我认为这是不正确的。考虑:

 #include <stdio.h>
 struct Baz { int quux; };

 void foo(Baz *arg1, Baz *arg2)
 {
    arg1 = arg2;
 }

int main(int argc, char *argv[])
{
    Baz arg1{1};
    Baz arg2{2};
    foo(&arg1, &arg2);
    printf("Arg 1 : %d, arg 2: %d\n", arg1.quux, arg2.quux);
    return 0;
}

输出:

Arg 1 : 1, arg 2: 2

这是按值传递的C-ish示例。将指针分配给函数内部的另一个指针时,外部没有任何变化。现在很明显,您可以取消引用指针并更改其指向的内容,但这是在修改传递的值,而不是重新分配它。当您尝试在Java中重新分配函数参数时,也会发生这种情况。 see here。它按值传递引用,而不是按引用传递。

C ++确实具有真正的引用传递功能。

#include <stdio.h>
struct Baz
{
    int quux;
};

void foo(Baz& arg1, Baz& arg2)
{
    arg1 = arg2;
}

int main(int argc, char *argv[])
{
    Baz arg1{1};
    Baz arg2{2};
    foo(arg1, arg2);
    printf("Arg 1 : %d, arg 2: %d\n", arg1.quux, arg2.quux);
    return 0;
}

输出:

Arg 1 : 2, arg 2: 2

函数内部参数的分配反映在调用方中。