如果不在内存中,表达式和常量存储在哪里?

Aqu*_*irl 47 c c++

来自Brian W. Kernighan的C编程语言

&运算符仅适用于内存中的对象:变量和数组元素.它不能应用于表达式,常量或寄存器变量.

如果不在内存中,表达式和常量存储在哪里?这句话是什么意思?

例如:
&(2 + 3)

为什么我们不能拿它的地址?它存放在哪里?
对于C++,答案是否也一样,因为C一直是它的父级?

这个链接的问题解释了这样的表达式是rvalue对象,并且所有rvalue对象都没有地址.

我的问题是这些表达式存储在哪里,以至于无法检索到它们的地址?

aaa*_*789 58

考虑以下功能:

unsigned sum_evens (unsigned number) {
  number &= ~1; // ~1 = 0xfffffffe (32-bit CPU)
  unsigned result = 0;
  while (number) {
    result += number;
    number -= 2;
  }
  return result;
}
Run Code Online (Sandbox Code Playgroud)

现在,让我们玩编译器游戏并尝试手动编译.我假设您正在使用x86,因为这是大多数台式计算机使用的.(x86是Intel兼容CPU的指令集.)

让我们通过一个简单的(未经优化的)版本来解释这个例程在编译时的样子:

sum_evens:
  and edi, 0xfffffffe ;edi is where the first argument goes
  xor eax, eax ;set register eax to 0
  cmp edi, 0 ;compare number to 0
  jz .done ;if edi = 0, jump to .done
.loop
  add eax, edi ;eax = eax + edi
  sub edi, 2 ;edi = edi - 2
  jnz .loop ;if edi != 0, go back to .loop
.done
  ret ;return (value in eax is returned to caller)
Run Code Online (Sandbox Code Playgroud)

现在,你可以看到,在代码中的常数(0,2,1)实际上显示为的CPU指令的一部分!事实上,1根本没有出现; 编译器(在这种情况下,只是我)已经计算~1并使用代码中的结果.

虽然你可以获取CPU指令的地址,但取一部分的地址通常是没有意义的(在x86中你有时可以,但在许多其他的CPU中你完全不能这样做),并且代码地址是从根本上不同于数据地址(这就是为什么你不能将函数指针(代码地址)视为常规指针(数据地址)).在某些CPU架构中,代码地址和数据地址完全不兼容(尽管大多数现代操作系统使用它的方式与x86不同).

请注意while (number)相当于while (number != 0).这0根本不会出现在已编译的代码中!它是由jnz指令暗示的(如果不是零则跳转).这就是为什么你不能拿出那个地址的另一个原因0- 它没有一个,它实际上无处可去.

我希望这能让你更清楚.

  • 我想我的意思是这个答案或多或少都有它倒退 - 你正在研究编译器*在给定语言的*actual*语义的情况下表现的方式,然后将其作为那些语义的基本原理.但编译器将(并且可能)生成实现语言语义所需的任何机器代码 - 没有内在/物理原因,为什么一个*不能*取表达式的地址等. (6认同)
  • 然而,重复你之前的评论 - 解释如何难以定义合理的语义(特别是在生命方面)来获取表达式的地址,这将是一个更好的答案(IMO;) (5认同)
  • (@aaa:在某处删除对GodBolt的引用:https://godbolt.org/)(你需要`int result`) (4认同)
  • @OliverCharlesworth在这个例子中明确适用,因为`result`也没有地址.但是,如果编译器被强制给它一个,那么如何处理变量的语义是清楚的.在这方面,常数更加尴尬.但是你_can_做`((unsigned []){3})`如果你需要一个指向常量3的指针 - 例如 - 这是由语言很好地定义的.(至少在C中;在这里不确定C++.) (3认同)
  • 许多命名变量也可以这样说 - 它们也可能不存在于主存中.也就是说,直到编译器被迫将它们放在那里因为你拿走了它们的地址.所以不清楚为什么同样不适用于表达式. (2认同)
  • 如果你使用`test edi,edi`而不是`cmp edi,0`,你将在汇编代码中使得不存在常量"0"的点更加有效. (2认同)
  • @briantist是建议编辑的一部分,而x86则部分错误.通过手册,这是真的; 代码和数据存在于不同的段中,每个段都有自己的寻址空间.但是大多数现代操作系统使用CPU的方式是使所有段指向相同的内存区域,从而使数据指针(来自数据段)和代码指针(来自代码段)指向内存的同一部分在数值上是平等的.据我所知,Windows和Linux都是这样做的.我编辑了帖子以使其更清楚. (2认同)

Use*_*ess 38

存储这些表达式的位置,以便无法检索地址?

你的问题不是很好.

  • 概念

    这就像问为什么人们可以讨论名词的所有权而不是动词.名词指的事情,可能(潜在)所拥有,而动词指行动所执行的.你不能拥有一个动作或执行一件事.

  • 在语言规范方面

    表达式不存储在第一位,它们被评估.编译器可以在编译时对它们进行评估,也可以在运行时由处理器对它们进行评估.

  • 在语言实施方面

    考虑一下这句话

    int a = 0;
    
    Run Code Online (Sandbox Code Playgroud)

    这样做有两件事:首先,它声明一个整数变量a.这被定义为您可以采取的地址.编译器可以在给定平台上执行任何有意义的操作,以允许您获取地址a.

    其次,它将变量的值设置为零.这并不会与零值在编译的程序存在于某个地方的意思是一个整数.它通常可以实现为

    xor eax,eax
    
    Run Code Online (Sandbox Code Playgroud)

    也就是说,XOR(异或)eax寄存器本身.这总是导致零,无论以前是什么.但是,0编译后的代码中没有固定的有价值对象来匹配0您在源代码中编写的整数文字.

顺便说一句,当我说a上面的内容是你可以采取的地址时 - 值得指出的是,除非你接受它,否则它可能没有真正的地址.例如,该eax示例中使用的寄存器没有地址.如果编译器可以证明程序仍然正确,那么它a可以在该寄存器中存活并且永远不会存在于主存储器中.相反,如果您在&a某处使用表达式,编译器将注意创建一些可存储空间来存储其a值.


请注意,我可以轻松地选择一种不同的语言,我可以使用表达式的地址.

它可能会被解释,因为一旦机器可执行输出替换它们,编译通常会丢弃这些结构.例如,Python具有运行时内省和code对象.

或者我可以从LISP开始并扩展它以提供对S表达式的某种操作地址.

它们两者的共同关键是它们不是C,这在设计和定义方面并不提供这些机制.

  • 除此之外,我认为这是最好的答案,到目前为止 - 它是唯一一个说(或多或少)"因为标准这样说",而不是将实现细节视为内在的证明/物理限制. (8认同)
  • 这在术语上有点松散 - 在语言标准的眼中,“对象”!=“变量”。临时对象也是一个对象。 (2认同)

Lun*_*din 10

这些表达式最终成为机器代码的一部分.表达式2 + 3可能被转换为机器代码指令"将5加载到寄存器A".CPU寄存器没有地址.

  • 理论上,如果他们最终在机器代码中被翻译,那么他们应该在.text部分占用空间.但他们没有!为什么会这样? (3认同)
  • @Gaurav:"他们"是什么?在Lundin的例子中,数字"5"可能作为文字操作数出现,作为更大的机器代码指令的一部分.指令的某些部分也没有地址.(如果你要挑选语义.他们*做*但你无法访问它.)(Nitpick#2:某些架构可能不会将实际数字5作为一个字节存储起来.)(Nitpick#3 :根据具体情况,在指令本身中,数字"5"可能根本不会出现*.考虑`a = 5*b;`可以编译为`lea eax,[ebx + 4*ebx]`. ) (3认同)
  • 对于命名变量也可以这样说,所以我不确定这是一个很好的解释. (2认同)
  • 我没有挑剔.这只是出于好奇和缺乏知识,你可以说. (2认同)

klu*_*utt 5

将地址用于表达式并没有多大意义.你能做的最接近的是一个函数指针.表达式的存储方式与变量和对象的存储方式不同.

表达式存储在实际的机器代码中.当然,您可以找到评估表达式的地址,但这样做是没有意义的.

阅读有关装配的一些内容.表达式存储在文本段中,而变量存储在其他段中,例如数据或堆栈.

https://en.wikipedia.org/wiki/Data_segment

解释它的另一种方法是表达式是cpu指令,而变量是纯数据.

还有一件事要考虑:编译器经常优化掉东西.考虑以下代码:

int x=0;
while(x<10)
    x+=1;
Run Code Online (Sandbox Code Playgroud)

此代码将被优化为:

int x=10;
Run Code Online (Sandbox Code Playgroud)

那么(x+=1)在这种情况下,地址意味着什么呢?它甚至不存在于机器代码中,因此根据定义它根本没有地址.