是否可以在没有指针的情况下实现引用传递

Could Pass-by-Reference be implemented without pointers?

本文关键字:实现 情况下 引用 指针 是否      更新时间:2023-10-16

这可能是一个关于计算机体系结构的问题,而不是c++本身的问题,但我想知道在像c++这样的语言中,如果不使用指针,理论上是否可以实现引用传递。

下面是三个具有类似功能和结构的代码示例。

//Version 1: Uses global variable
#include <iostream>
int global_var = 0;
int increment(void)      {return ++global_var;}
int main(void)
{
    while (global_var < 100)
        printf("Counter: %in", increment());
    return 0;
}

//Version 2: Uses pass-by-pointer
#include <iostream>
int increment(int* ptr)   {return ++(*ptr);}
int main(void)
{
    int local_var = 0;
    while (local_var < 100)
        printf("Counter: %in", increment(&local_var));
    return 0;
}

//Version 3: Uses pass-by-reference
#include <iostream>
int increment(int& ref)   {return ++ref;}
int main(void)
{
    int local_var = 0;
    while (local_var < 100)
        printf("Counter: %in", increment(local_var));
    return 0;
}

我的猜测是第一个版本中的增量函数直接访问全局变量,没有任何指针。第二个版本在函数的堆栈帧中分配一个指向局部变量的指针,并间接访问它。通过快速搜索,第三个版本显然与第二个版本完全相同(无论如何,在优化之前)。来源:引用是如何在内部实现的?

但是在理论上 (甚至在实践中,经过一些编译器优化后),第三个函数可以直接访问自己的堆栈帧外的局部变量而不需要指针吗?或者这种行为专属于全局变量,因为它们在内存中的位置是静态的?

我问这个问题是因为我认为创建和解引用指针应该占用的时间和内存量。在传递12个引用的深层递归函数中,时间和内存可能会累积起来。

注:我还应该特别提到内联函数,因为它们甚至不生成新的堆栈框架。也就是说,如果函数是inline int increment(int*)inline int increment(int&),版本2和3的汇编代码会有所不同吗?或者编译器会在这种情况下优化指针吗?

由于不同的编译器可以处理不同的代码,因此我将向您展示msvc++如何处理以下代码的示例:

#include <iostream>
__declspec(noinline) int Increament(int* p)
{
    std::cout << "Increament Pointer called" << std::endl;
    ++*p;
    return *p;
}
__declspec(noinline) int Increament(int& p)
{
    std::cout << "Increament Reference called" << std::endl;
    ++p;
    return p;
}
int main()
{
    int x = 10;
    Increament(x);
    Increament(&x);

    std::cin.get();
    return 0;
}

可以看到,两个版本的increment()产生完全相同的代码,它将x的有效地址加载到寄存器eax中,并将该地址压入堆栈。

int x = 10;
00A52598  mov         dword ptr [x],0Ah  
    Increament(x);
00A5259F  lea         eax,[x]  
    Increament(x);
00A525A2  push        eax  
00A525A3  call        Increament (0A5135Ch)  
00A525A8  add         esp,4  
    Increament(&x);
00A525AB  lea         eax,[x]  
00A525AE  push        eax  
00A525AF  call        Increament (0A51357h)  
00A525B4  add         esp,4  

至于你的其他问题,编译器可以自由地做任何它喜欢做的事情,结果会彼此不同。

我发布这篇文章的原因是为了让你明白,在asm中没有引用,引用被视为指针,就编译器而言,事实上,引用是带有限制的指针,在c++中设计略有不同。

由于评论中的一些问题而更新:

使用全局变量是否会产生不同的程序集输出

1)代码中的第一个示例将产生不同的汇编,因为global_var是全局的,并且正在初始化,这将把它存储在数据段中。

让我们看一下:

#include <iostream>
int global_var = 50;
__declspec(noinline) int increment(void)
{ 
    ++global_var;
    return global_var;
}
int main(void)
{
    increment();
    std::cin.get();
    return 0;
}

为函数生成以下程序集,注意这里没有push任何内容:

00A61000  mov         eax,dword ptr ds:[00A63018h]  
00A61005  inc         eax  
00A61006  mov         dword ptr ds:[00A63018h],eax 

0x00A63018是内存中global_var的地址,它的值被存储在eax中,递增并恢复到旧的内存位置。

基本上不可能以同样的方式实现引用作为全局变量(即不带指针访问它们)

我不明白你的问题。你可以有全局引用,根据规则,它必须直接指向某些东西:例如,另一个初始化的变量,它将以相同的方式运行,除了保存一个值50,它将保存另一个全局变量的地址。

。在没有指针的情况下访问它们

这部分是什么让我困惑,这是没有意义的时候谈论参考。你不能通过指针访问引用,这在c++中是不可能的。你甚至不能得到一个参考的地址。

引用,像其他所有东西一样,通常在汇编语言内部实现,而不是作为C代码实现。

大多数汇编语言通过地址访问所有内容(即使是编译根本不使用指针或引用的C代码的结果),除了寄存器中有内容的短暂时间。

相关文章: