如何在程序集中编译c ++ by-ref参数传递?

时间:2009-05-08 14:15:00

标签: c++ assembly compiler-construction

在大学的最后几年,我有一门关于编译器的课程。我们为C的子集创建了一个编译器。我一直想知道如何在C ++中将pass-by-ref函数调用编译成汇编。

根据我的记忆,pass-by-val函数调用遵循以下过程:

  • 存储PP的地址
  • 将参数推送到堆栈
  • 执行函数调用
  • 在函数中,从堆栈弹出参数

传递参考有什么不同? (int void(int&);)

修改

我可能听起来完全迷失了但是,如果你能帮助我,我会非常感激。

每个人的答案基本上都是传递地址而不是值。我明白这基本上是指针传递的东西。那么,为什么这两个函数表现不同?:

struct A {
    int x;
    A(int v){
        x = v;
    }
};

int byRef(A& v){
    v = A(3);
    return 0;
}

int byP   (A* v){
    v = &A(4); //OR new A(4)
    return 0;
}

int _tmain(int argc, _TCHAR* argv[])
{
    A a (1); A b (2);
    byRef(a); byP  (&b);
    cout << a.x << " " << b.x;

    system("pause");

    return 0;
}

我知道在byP(A *)中,v是按值传递的,因此,它不会影响调用者的参数。那么,你如何用A *?

来实现byRef(A&amp;)

8 个答案:

答案 0 :(得分:6)

您将指针传递给referand,就像您使用任何其他指针一样,并且被调用者知道如何使用它。因此,根据实现,它可能不在堆栈中 - 某些参数在某些调用约定中的寄存器中传递。

可能有其他方法可以做到这一点,因为C ++标准没有指定如何实现引用,即使它们是作为指针实现的,我想它们可能在调用约定中被区分。但是,指针是最明显的实现方式。

答案 1 :(得分:4)

通过引用调用将涉及将指针传递给值而不是值本身的副本。如果您对血腥细节感兴趣,可以让编译器发出汇编语言并自行检查。

编辑:您的指针示例应该是:

int byP (A* v) {
    * v = A(4);    // modify thing referenced by the pointer
    return 0;
}

答案 2 :(得分:4)

int byRef(A& v){
  v = A(3);
  return 0;
}

这将调用临时对象到通过引用传递的对象,修改函数调用中使用的对象。如果没有提供赋值运算符,则将执行浅拷贝。

int byP   (A* v){
  v = &A(4); //OR new A(4)
  return 0;
}

这会将指向临时对象的指针复制到传入的指针值。没有调用赋值函数。 “v”的值已更改,但对象v指向,作为参数传递的对象地址未更改。

如果你这样做了:

struct A {
  int x;
  A(int v){
    x = v;
  }
  A &operator = (A &rhs){
    cout << "assignment!";
  }
};

然后“赋值”将在byRef函数中输出,但不会在byP函数中输出。

尽管&是使用“引擎盖下的指针”实现的,正如其他人所说,它们被视为通过语言传递给函数的对象。

所以,使用指针实现byRef

int byRefUsingP (A *v)
{
  *v = A(3);
  // or you could do:
  // v->operator = (A(3));
  // if an operator = is defined (don't know if it will work without one defined)
  return 0;
}

答案 3 :(得分:0)

不同之处在于它传递参数的地址,而不是参数的值。

答案 4 :(得分:0)

我没有看过类似GCC的实现,但一般的想法是,不是传递变量的内容,而是传递其地址(就像C ++代码使用“*”而不是参数上的“&amp;”。

其他方案可用,但通常可以通过推送堆栈上的值(或地址)来完成。

答案 5 :(得分:0)

我认为在pass-by-val中,程序将是:     *存储PP的值     *将参数推入堆栈     *执行功能调用     *在函数中,从堆栈弹出参数

并且在pass-by-ref中,程序将是您编写的程序

欢呼声

答案 6 :(得分:0)

你可以自己看看。许多调试器允许您在代码和反汇编视图之间切换。我在代码视图中的函数调用上设置了一个断点,运行app到那一点,切换到程序集,然后跳过每个命令。

[SPOILER ALERT]

这是一个指针。

答案 7 :(得分:0)

当通过值传递时,编译器会发出代码以将源字节按位复制到目标变量。对于大型结构和/或频繁分配,这可能会非常低效。如果你的结构覆盖赋值运算符,它将被调用,然后你可以控制复制的内容,但是你仍然在堆栈上推送整个结构。

当通过引用传递时,struct的地址被压入堆栈,这只是一个int。这是传递大型对象的最有效方法。

当通过指针传递时,指针的地址被推送到堆栈上。必须取消引用指针才能获取结构的地址。还有一项额外的操作。

顺便说一下,byP函数中存在一个严重错误:它正在为指针分配一个临时局部变量。结构在堆栈上分配,并且很可能在它超出范围后被覆盖。根据Neil Butterworth的例子,您必须使用“new”在堆上分配它,或者将结构分配给指针引用的值。