3
int x = 12;

12据说是整数文字,因此不能在 LValue 中使用。

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

6 回答 6

4

好的问题中的坏例子。
但问题仍然有效:
让我们试试:

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 中不是临时对象。
它是一个整数文字。

于 2011-12-20T07:17:00.750 回答
3

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

int x = 12 

会转化为类似的东西

movl 12 address_of_x_or_register

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

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

于 2011-12-20T07:00:23.920 回答
2

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

string myString = "hello";

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

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不是一个对象,它是给转换构造函数的参数

于 2011-12-20T07:34:50.217 回答
1

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

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

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

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

于 2011-12-20T07:13:16.530 回答
1

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

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

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 都将是指令的一部分(编译代码块)。因此它不会是一个单独的对象。

于 2011-12-20T07:58:05.613 回答
1

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

文字的范围是什么?

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

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

于 2011-12-21T10:57:16.910 回答