Key*_*lug 28 c++ pointers reference
只是想知道它是如何在不同的编译器和调试/发布配置中实现的.标准是否以某种方式提供有关其实施的建议?它在哪里有所不同?
我试图运行一个简单的程序,我已经从函数返回非const引用和指向局部变量的指针,但它的工作方式相同.那么内部引用只是一个指针是真的吗?
Sin*_*ion 45
只是重复一些人们一直在说的东西,让我们看看一些编译器输出:
#include <stdio.h>
#include <stdlib.h>
int byref(int & foo)
{
printf("%d\n", foo);
}
int byptr(int * foo)
{
printf("%d\n", *foo);
}
int main(int argc, char **argv) {
int aFoo = 5;
byref(aFoo);
byptr(&aFoo);
}
Run Code Online (Sandbox Code Playgroud)
我们可以用LLVM编译它(关闭优化),我们得到以下结果:
define i32 @_Z5byrefRi(i32* %foo) {
entry:
%foo_addr = alloca i32* ; <i32**> [#uses=2]
%retval = alloca i32 ; <i32*> [#uses=1]
%"alloca point" = bitcast i32 0 to i32 ; <i32> [#uses=0]
store i32* %foo, i32** %foo_addr
%0 = load i32** %foo_addr, align 8 ; <i32*> [#uses=1]
%1 = load i32* %0, align 4 ; <i32> [#uses=1]
%2 = call i32 (i8*, ...)* @printf(i8* noalias getelementptr inbounds ([4 x i8]* @.str, i64 0, i64 0), i32 %1) ; <i32> [#uses=0]
br label %return
return: ; preds = %entry
%retval1 = load i32* %retval ; <i32> [#uses=1]
ret i32 %retval1
}
define i32 @_Z5byptrPi(i32* %foo) {
entry:
%foo_addr = alloca i32* ; <i32**> [#uses=2]
%retval = alloca i32 ; <i32*> [#uses=1]
%"alloca point" = bitcast i32 0 to i32 ; <i32> [#uses=0]
store i32* %foo, i32** %foo_addr
%0 = load i32** %foo_addr, align 8 ; <i32*> [#uses=1]
%1 = load i32* %0, align 4 ; <i32> [#uses=1]
%2 = call i32 (i8*, ...)* @printf(i8* noalias getelementptr inbounds ([4 x i8]* @.str, i64 0, i64 0), i32 %1) ; <i32> [#uses=0]
br label %return
return: ; preds = %entry
%retval1 = load i32* %retval ; <i32> [#uses=1]
ret i32 %retval1
}
Run Code Online (Sandbox Code Playgroud)
两个功能的主体是相同的
Pet*_* G. 15
引用的自然实现确实是一个指针.但是,请不要在代码中依赖于此.
Pra*_*ane 14
很抱歉使用汇编来解释这一点,但我认为这是了解编译器如何实现引用的最佳方法.
#include <iostream>
using namespace std;
int main()
{
int i = 10;
int *ptrToI = &i;
int &refToI = i;
cout << "i = " << i << "\n";
cout << "&i = " << &i << "\n";
cout << "ptrToI = " << ptrToI << "\n";
cout << "*ptrToI = " << *ptrToI << "\n";
cout << "&ptrToI = " << &ptrToI << "\n";
cout << "refToNum = " << refToI << "\n";
//cout << "*refToNum = " << *refToI << "\n";
cout << "&refToNum = " << &refToI << "\n";
return 0;
}
Run Code Online (Sandbox Code Playgroud)
这段代码的输出是这样的
i = 10
&i = 0xbf9e52f8
ptrToI = 0xbf9e52f8
*ptrToI = 10
&ptrToI = 0xbf9e52f4
refToNum = 10
&refToNum = 0xbf9e52f8
Run Code Online (Sandbox Code Playgroud)
让我们看看反汇编(我为此使用了GDB.这里是8,9和10是代码的行号)
8 int i = 10;
0x08048698 <main()+18>: movl $0xa,-0x10(%ebp)
Run Code Online (Sandbox Code Playgroud)
这$0xa是我们分配给的10(十进制)i.-0x10(%ebp)这里表示ebp register-16(十进制)的内容.
-0x10(%ebp)指向i堆栈的地址.
9 int *ptrToI = &i;
0x0804869f <main()+25>: lea -0x10(%ebp),%eax
0x080486a2 <main()+28>: mov %eax,-0x14(%ebp)
Run Code Online (Sandbox Code Playgroud)
指定的地址i来ptrToI.ptrToI再次位于地址的堆栈上-0x14(%ebp),即ebp- 20(十进制).
10 int &refToI = i;
0x080486a5 <main()+31>: lea -0x10(%ebp),%eax
0x080486a8 <main()+34>: mov %eax,-0xc(%ebp)
Run Code Online (Sandbox Code Playgroud)
现在抓住了!比较第9行和第10行的反汇编,您将观察到,在行号10中被-0x14(%ebp)替换为地址.它在堆栈上分配.但是您永远无法从代码中获取此地址,因为您无需知道地址.-0xc(%ebp)-0xc(%ebp)refToNum
所以; 引用确实占用了内存.在这种情况下,它是堆栈内存,因为我们已将其分配为局部变量.它占用了多少内存?指针占据了很多.
现在让我们看看我们如何访问引用和指针.为简单起见,我只展示了部分组装片段
16 cout << "*ptrToI = " << *ptrToI << "\n";
0x08048746 <main()+192>: mov -0x14(%ebp),%eax
0x08048749 <main()+195>: mov (%eax),%ebx
19 cout << "refToNum = " << refToI << "\n";
0x080487b0 <main()+298>: mov -0xc(%ebp),%eax
0x080487b3 <main()+301>: mov (%eax),%ebx
Run Code Online (Sandbox Code Playgroud)
现在比较以上两行,你会看到惊人的相似性. -0xc(%ebp)是refToI您永远无法访问的实际地址.简单来说,如果您将引用视为普通指针,那么访问引用就像获取引用指向的地址处的值一样.这意味着以下两行代码将为您提供相同的结果
cout << "Value if i = " << *ptrToI << "\n";
cout << " Value if i = " << refToI << "\n";
Run Code Online (Sandbox Code Playgroud)
现在比较一下
15 cout << "ptrToI = " << ptrToI << "\n";
0x08048713 <main()+141>: mov -0x14(%ebp),%ebx
21 cout << "&refToNum = " << &refToI << "\n";
0x080487fb <main()+373>: mov -0xc(%ebp),%eax
Run Code Online (Sandbox Code Playgroud)
我猜你能发现这里发生了什么.如果您要求&refToI,-0xc(%ebp)地址位置的内容将 被返回,并且-0xc(%ebp)位于其中refToi,其内容只是地址i.
最后一件事,为什么这一行被评论了?
//cout << "*refToNum = " << *refToI << "\n";
Run Code Online (Sandbox Code Playgroud)
因为*refToI不允许,它会给你一个编译时错误.
用比耶恩的话说:
像指针一样,引用是对象的别名,通常被实现为保存对象的机器地址,并且与指针相比不增加性能开销,但是与指针的不同之处在于:
•您使用与对象名称完全相同的语法访问引用。
•引用始终引用对其进行初始化的对象。
•没有“空引用”,我们可以假定引用是指对象
尽管引用实际上是一个指针,但是它不应像指针一样使用,而是用作别名。
| 归档时间: |
|
| 查看次数: |
9380 次 |
| 最近记录: |