内存中的 C++ 文字常量存储在哪里?堆栈还是堆?
int *p = &2
是错的。我想知道为什么?谢谢
-------------------------------------------------
我的问题是“C++ 文字常量存储在内存中的位置”,“int *p = &2
错了”,不是我的问题。
细节取决于机器,但假设最常见的机器和操作系统......每个可执行文件都包含几个“段” - CODE、BSS、DATA 和其他一些。
CODE 保存所有可执行的操作码。实际上,它通常被命名为 TEXT,因为不知何故这对几十年前的人们来说是有意义的。通常它是只读的。
BSS 是未初始化的数据——它实际上不需要存在于可执行文件中,而是在程序开始运行时由操作系统的加载器分配。
DATA 保存文字常量 - int8、int16、int32 等以及浮点数、字符串文字,以及编译器和链接器想要产生的任何奇怪的东西。这就是你要问的。但是,它只包含定义为用作变量的常量,如
const long x = 2;
但不太可能保存源代码中使用但与变量不紧密关联的文字常量。编译器直接处理一个单独的“2”。例如在 C 中,
print("%d", 2);
将导致编译器构建对 print() 的子例程调用,编写操作码以将指针推送到字符串字面量“%d”和值 2,两者都是 64 位机器上的 64 位整数(你不是那些仍然使用 32 位硬件的落后者之一,是吗?:) 后跟操作码以跳转到子程序('print' 子程序的标识符)。
"%d" 文字进入 DATA。2没有;它内置在将整数塞入堆栈的操作码中。这实际上可能是“加载寄存器 RAX 立即数”,然后是值 2,然后是“推送寄存器 RAX”,或者单个操作码可以完成这项工作。所以在最终的可执行文件中,2 将在 CODE(又名 TEXT)段中找到。
通常不可能创建指向该值或任何操作码的指针。就 C 之类的高级语言所做的事情而言,它只是没有意义(当您谈论操作码和段时,C 是“高级”。)“&2”只能是一个错误。
现在,拥有指向操作码的指针并非完全不可能。每当您在 C 中定义函数,或在 C++ 中定义对象方法、构造函数或析构函数时,函数的名称都可以被认为是指向从该函数编译的机器代码的第一个操作码的指针。例如,不带括号的 print() 是指向函数的指针。也许如果您的示例代码在一个函数中并且您猜到了正确的偏移量,则可以使用指针算术来指向位于操作码中的那个“立即”值 2,但这对于任何当代 CPU 来说都不容易,当然不适合初学者。
让我引用C++03标准的相关条款。5.3.1/2
一元 & 运算符的结果是指向其操作数的指针。操作数应为左值。
整数文字是一个右值(但是,我在 C++03 标准中没有找到直接引用,但 C++11 在 3.10/1 中作为旁注提到了这一点)。因此,不可能采用整数文字的地址。
存储的确切位置如何2
,这取决于使用情况。它可能是机器指令的一部分,也可能被优化掉,例如j=i*2
可能变成j=i+i
. 你不应该依赖它。
你有两个问题:
文字常量存储在哪里?除了字符串文字(它们是实际的对象)之外,几乎可以在实现所需的任何地方进行。它通常取决于你对它们做什么,但在很多架构上,整数常量(通常还有一些特殊的浮点常量,比如0.0
)最终会成为机器指令的一部分。当这不可能时,它们通常会被放置在与代码相同的逻辑段中。
至于为什么取一个右值的地址是非法的,主要是因为标准是这么说的。从历史上看,这是被禁止的,因为这样的常量通常永远不会作为单独的对象存在于内存中,因此没有地址。今天......人们可以想象其他解决方案:编译器足够聪明,如果您获取它们的地址,则可以将它们放入内存中,否则不会;并且类类型的右值确实有一个内存地址。这些规则有些武断(无论它们是什么)——希望任何允许获取文字地址的规则都会使其成为 type int const*
,而不是int*
。