我对 C++ 的工作原理感到困惑。
据我了解,C++ 语言具有以下等价性: 变量(名称)<=>内存中的数据。 (例如:int a = 5; // a <=> 存储在内存中的该变量名称的值,此处为 5)。事实上,即使 assembly 也不具有这种等价性,标签对应于 数据的地址。
顺便说一句,我很难说服自己在 C++ 中需要这种等价性(不是贬义,因为我确信这是有原因的)。 (在有效的 C++ 中,它表示通过 const ref 传递每个对象,然后查看我们需要复制对象值的哪一部分,也许是全部)。
也就是说,这种推理证明了对指针的需要(和引用,据我所知,*具有更好语义的常量指针)。
*不完全是 C++ 意义上的,因为它们可能仅存储在其中一个寄存器中,因此在堆栈上没有地址 -> 不是真正的对象。 (观察编译后的 C++ 代码后,在编译器没有“危险信号”的情况下,指针的情况也是如此,例如带有 &ptr 的表达式,它告诉它 ptr 必须确实在内存中有一个地址)。另一种理解引用的方法(我发现这种方法效果很好)是认为它是对象的另一个名称,并且类型不同只是因为初始化(不同)和未经授权的操作(例如重新分配) 。然而,在我看来,这种推理是有缺陷的,因为它使我们无法理解为什么通过引用传递有时比通过值传递更经济。
无论如何,在重新定义我的第一个运算符之后,我意识到这一行推理是有意义的(对于原始类型和对象):事实上,运算符将对象地址作为参数,这是访问它们的值所必需的。因此,我们推断创建了 2 个右值并(通常)存储在寄存器中:2 个地址(具有 C++ 中的引用类型)。然后,运算符“函数”使用 2 个地址将其中一个的值(<=> C++ 中的变量名称)分配给另一个。
但是后来我遇到了一个我对 C++ 的理解无法解决的问题: *ptr 具有对象的类型(例如 int 而不是 int&),但它是对对象的引用(其在内存中的地址)。
我想我完全迷失了!
有人可以通过给我一个在所有情况下都有效的“权威”解释来帮助我理解指针和引用以及如何在低层次上解释它们吗?
变量(名称)<=>内存中的数据
有很多例外情况。
匿名临时对象有存储但没有命名
动态分配的对象没有名称(但可能有一个指向该对象的指针,该对象可能有名称)
标准已尽我所能,在 [intro.object]:
根据“as-if”规则,允许实现在同一机器地址存储两个对象,或者如果程序无法观察到差异,则根本不存储对象
考虑表达式、它们的类别、类型以及它们识别的对象可能更有用。
*ptr 具有对象的类型(例如 int 而不是 int&),但它是对对象的引用(其在内存中的地址)。
来自 [expr.type] 的另一个相对友好的标准片段:
如果表达式最初的类型为“对 T 的引用”(...),则在进行任何进一步分析之前,会将类型调整为 T。 表达式指定引用所表示的对象或函数
所以
*ptr
只是一个参考。当计算表达式时,引用“跟随”到所引用的对象。这与通过原始名称引用对象的语法相同,但显然不完全相同:该对象可能来自不同的范围,甚至可能没有名称。
标识符、引用和指针的概念之间存在某种关系 - 但它非常抽象。
标识符绑定到声明的范围。您无法传递标识符或重新安装它。
它纯粹是一个本地标签,除了调试信息之外,它只在编译时存在。
引用与标识符具有相同的语法,并且在使用时计算为相同的类型,但也可以在声明范围之外传递。
它是实现定义的引用所需的存储(如果有),但它是某种可以在运行时传递的实体。
指针是一等对象,而引用却不是。您可以传递指针(通过值或引用)、改变它们、获取它们的地址等。