【问题标题】:What is the scope of a literal value, and how does the compiler allocate memory to it?文字值的范围是什么,编译器如何为其分配内存?
【发布时间】:2026-02-16 02:30:01
【问题描述】:
int x = 12;

12 被称为整数文字,因此不能在 LValue 中使用。

  1. 编译器如何为文字分配内存?
  2. 文字的范围是什么?
  3. 为什么我们不能在其范围内获取带有 &12 的地址?

【问题讨论】:

  • Err,12 根本不是 objectx 也不是。你可能想选择一个更好的例子,比如 real 对象。
  • 这是一个完整的文字 - 请参阅 skjaidev 的回答。
  • x 确实在 C++ 对象模型下命名了一个对象。它由定义创建,具有存储期限,并且具有类型。请参阅 C++11 标准中的 § 1.8 C++ 对象模型。在这个问题中,很明显“对象”一词是在这个意义上使用的,而不是在面向对象编程中的对象意义上。
  • @paxdiablo 相关引用是 §1.8.1 “C++ 程序中的构造创建、销毁、引用、访问和操作对象。对象是存储区域。”
  • @AnishaKaul:int 是一个类型,而不是一个类。在 C 中,没有类这样的东西。在 C++ 中,类类型是使用 structclassunion 声明的聚合类型。

标签: c++ c object scope rvalue


【解决方案1】:

OK 问题中的错误示例。
但问题仍然有效:
让我们试试:

Foo getFoo() {return Foo();}

int func()
{
    getFoo().bar();   // Creates temporary.
    // before this comment it is also destroyed.
    // But it lives for the whole expression above
    // So you can call bar() on it.
}

int func2()
{
    Foo const& tmp = getFoo();  // Creates temporary.
                                // Does not die here as it is bound to a const reference.

    DO STUFF
}  // tmp goes out of scope and temporary object destroyed.
   // It lives to here because it is bound to a const reference.

编译器如何为临时对象分配内存?

在编译器之前未定义。
但是在堆栈帧上分配一点点更多的内存并将其保存在那里真的很容易。然后销毁它并减小堆栈帧的大小(尽管这个答案对你永远不应该做的底层硬件做出了很多假设(最好只是把它想象成编译器在做魔术)。

临时对象的作用域是什么?

除非绑定到 const 引用,否则临时对象将一直存在到表达式的末尾(通常是 ;)。如果它被绑定到一个 const 引用,那么它也存在于该引用所属范围的末尾(有一些例外(如构造函数))。

为什么我们不能在其范围内获得 &12 的地址?

在问题 12 中不是临时对象。
它是一个整数文字。

【讨论】:

  • 洛基,一个有用的答案,谢谢。 @skjaidev 和 Loki,顺便说一句,编译器将 12 存储在目标文件而不是 RAM 中是否有一些特殊原因?
  • @AnishaKaul: 12 是一个整数文字,它可能不会存储在任何地方(它是代码的一部分)。变量x 是存储区域(一个对象),因此是可寻址的,这意味着 RAM 与否完全未定义。您的问题是您正在考虑特定的硬件要求。该标准故意试图避免硬件细节(如 RAM 的概念)。变量x如果使用它的地址,只需要有物理地址即可;它可能只是存在于一个寄存器中(但这偏离了标准中未解决的硬件细节)。
  • 12 is an integer literal it is probably not stored anywhere 如果我们直接写 12 而不是将其分配给某个东西,这可能就是我们得到错误的原因。那么,像 12 这样的文字不会存储在任何地方是答案吗?那么这是否意味着 12 不会被写入目标文件,但保存 12 的变量的值会得到?
  • @AnishaKaul: int main() {12;} 编译得很好。由于 12 是文字,因此是一个有效的表达式,它是一个有效的语句。请注意,它没有多大作用。文字 12 可能被编码为目标文件中的汇编指令。您中的变量x 是一个存在的对象(可能在内存中)。
  • 标准故意不指定这样的东西(它试图与硬件非常无关)。因此,这完全取决于编译器。
【解决方案2】:

在您的示例中,12 是一个整数文字。整数文字几乎总是作为立即操作数“嵌入”在机器指令中。它们不存储在内存中,因此您无法通过地址访问它,它们也没有范围。

int x = 12 

会翻译成类似的东西

movl 12 address_of_x_or_register

在几乎所有架构中。这里 12 被编码为指令的一部分。

需要明确的是,x 仍然驻留在内存中(局部变量的堆栈或全局变量的数据段)并最终包含值 12。 RHS“对象” 12 是整数文字,它在指令之前或期间不驻留在内存中,而是“驻留在”指令本身中。

【讨论】:

  • 那么,在编译过程中,编译器对它做了什么?存储?
  • @skjaidev BSS 代表什么?我猜的东西部分
  • 编译器直接在机器指令中使用该值,但这仅适用于整数。例如,字符串文字被分配到数据段中的存储空间。
  • @skjaidev 你的意思是说编译器在实际“编译”代码时,直接将 12 放在生成的目标文件中? 12 没有放在 RAM 中?
  • 为了清楚起见,x 仍然驻留在内存中(局部变量的堆栈或全局段的数据段)并在指令执行后包含值 12。 RHS 值12 在指令之前/期间并不驻留在内存中,而是驻留在指令本身中。
【解决方案3】:

1. How does the compiler allocate memory to a temporary object?

string myString = "hello";

在这种情况下,调用conversion constructor 来初始化一个新对象。

2. What is the scope of a temporary object?

临时对象的寿命直到分号

3. Why can't we get its address with an &12 in its scope? (in this case "hello")

&12不是对象,是给转换构造函数的参数

【讨论】:

  • &12 is not an object, it is the parameter given to the conversion constructor 为什么我们不能用 &12 得到 12 的地址?
  • 什么是“转换构造函数”?
  • 此链接有助于理解“转换构造函数”publib.boulder.ibm.com/infocenter/lnxpcomp/v8v101/…
  • 看到这个 SO 问题,为什么你不能取数字文字的地址 *.com/questions/1166378/…
  • 第二个链接显示了为什么您可以获取文字地址而不是反之亦然。 :(
【解决方案4】:

那么,在编译过程中,编译器对它做了什么?贮存?

这真的很难回答。编译器会尽可能地优化代码(优化的数量或级别通常可以在您的编译器工具中设置)。

对于给定的示例,如果 x 可以在汇编指令中被 12 替换,则 x 被“优化”并且 12 被用作 x 的直接替换。

“volatile”关键字可用于诱使编译器在每次在代码中使用 x 时检查它。这将生成更大的程序,但可能有助于您为变量分配内存位置(存储)。

【讨论】:

    【解决方案5】:

    对象是“内存中的东西”。这意味着这个 something 占据了进程地址空间的一部分(在堆栈中或在“空闲”内存中)。这也意味着你可以得到它的地址,如果你想的话。

    临时对象只是对象。如果需要此类对象来计算表达式,编译器会生成代码来创建它们:

    void f(const string&);
    
    void g()
    {
         f(string("Hello"));
    }
    

    调用 f() 的表达式将导致生成以下代码:

    • 创建临时字符串对象。以 const char* 作为参数调用构造函数;
    • 以对该临时对象的引用作为参数调用 f();
    • 销毁临时字符串对象。

    其中的关键部分是在表达式评估结束时销毁临时对象。临时对象只存在于表达式本身(即它们的临时作用域)。您可以尝试类似:const string* ps = &string("Hello") 但您的编译器可能会发出警报,因为这样的表达式会导致创建指针,该指针引用的内存 被临时对象占用但不再被它占用.它可能仍然包含字符串对象成员,也可能被程序中的以下表达式覆盖(更不用说销毁临时对象会释放对象在堆中分配的内存)。使用ps 会导致未定义的行为。

    这里出现的另一个问题是像int x = 12 这样的表达式中对象的性质。在这种情况下,x 可能是一个对象或可能不是。这取决于编译器设置和该行后面的代码。编译器可能会考虑将 x 放在寄存器中。在这种情况下 x 将不是对象,因为寄存器没有地址(至少在大多数平台上)。如果你不改变x 的值,编译器甚至可以直接在指令中使用12。所以“12”只会作为指令代码的一部分存在。

    无论如何,无论下面的代码发生什么,int x = 12 都不会创建临时的12 对象。如果编译器打算将 12 放在内存中,则指令将类似于(伪代码):

    store immediate address_of_x, 12
    

    如果在寄存器中显示 x,您将拥有:

    move immediate regX, 12
    

    在这两种情况下,12 都是指令的一部分(编译后的代码块)。因此它不会是一个单独的对象。

    【讨论】:

    • "临时对象仅存在于表达式本身(即它们的临时范围)中。" 临时对象未命名,因此它们没有任何关联的范围
    • @curiousguy 这就是为什么我称它为 makeshift 范围。计算表达式时存在临时对象。当程序停留在其范围内时,存在类似命名的对象。
    【解决方案6】:

    要小心,因为其他答案大多消除了您的误解,但不是这个:

    文字的范围是什么?

    这个问题没有意义,因为范围是名称的属性(变量名称、函数名称、类型名称、模板名称、命名空间名称...)。

    文字是数字的符号,而不是名称。

    【讨论】: