代码之家  ›  专栏  ›  技术社区  ›  Anzurio

汇编中编译的REF参数传递的C++是如何编译的?

  •  4
  • Anzurio  · 技术社区  · 16 年前

    在大学的最后几年,我上了一门编译课程。我们为C的一个子集创建了编译器。我一直想知道如何将C++中的Reby 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是通过值传递的,因此它不会影响调用方的参数。那么,您将如何实现ByRef(A&)中的a*?

    8 回复  |  直到 9 年前
        1
  •  4
  •   Tim Cooper    12 年前
    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 功能。

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

    因此,要实现 拜雷夫 使用指针:

    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;
    }
    
        2
  •  6
  •   Steve Jessop    16 年前

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

    可能还有其他方法来做这件事,因为C++标准没有指定如何实现引用,即使它们被实现为指针,我猜想它们可能会在调用约定中被区分。不过,指针是最明显的实现。

        3
  •  4
  •   anon    16 年前

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

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

    int byP (A* v) {
        * v = A(4);    // modify thing referenced by the pointer
        return 0;
    }
    
        4
  •  0
  •   Kevin Beck    16 年前

    区别在于它传递的是参数的地址,而不是参数的值。

        5
  •  0
  •   T.E.D.    16 年前

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

    其他方案也可用,但通常通过在堆栈上推送值(或地址)来实现。

        6
  •  0
  •   Arnkrishn    16 年前

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

    在旁路参考中,程序就是你写的程序。

    干杯

        7
  •  0
  •   Aardvark    16 年前

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

    [扰流板警报]

    .

    这是一个指针。

        8
  •  0
  •   alekop    15 年前

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

    当通过引用传递时,结构的地址被推到堆栈上,这只是一个int。这是传递大型对象的最有效方法。

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

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