如果不在内存中,表达式和常量存储在哪里?
Where are expressions and constants stored if not in memory?
来自 C 编程语言 作者:Brian W. Kernighan
& operator only applies to objects in memory: variables and array
elements. It cannot be applied to expressions, constants or register
variables.
如果不在内存中,表达式和常量存储在哪里?
那句话是什么意思?
例如:
&(2 + 3)
为什么我们不能获取它的地址?它存储在哪里?
C++ 的答案是否也一样,因为 C 是它的父级?
这链接 question explains 这样的表达式是 rvalue
个对象,所有 rvalue
个对象都没有地址。
我的问题是这些表达式存储在哪里,以至于无法检索它们的地址?
将地址取到表达式中并没有多大意义。您可以做的最接近的事情是函数指针。表达式的存储方式与变量和对象不同。
表达式存储在实际的机器代码中。当然,您可以找到计算表达式的地址,但这样做是没有意义的。
阅读一些关于汇编的内容。表达式存储在文本段中,而变量存储在其他段中,例如数据或堆栈。
https://en.wikipedia.org/wiki/Data_segment
另一种解释是,表达式是cpu指令,而变量是纯数据。
还有一件事需要考虑:编译器经常优化掉一些东西。考虑这段代码:
int x=0;
while(x<10)
x+=1;
此代码可能会优化为:
int x=10;
那么在这种情况下 (x+=1)
的地址是什么意思?它甚至不存在于机器代码中,因此根据定义,它根本没有地址。
这样的表达式最终成为机器代码的一部分。表达式 2 + 3
可能会被翻译成机器码指令 "load 5 into register A"。 CPU 寄存器没有地址。
考虑以下函数:
unsigned sum_evens (unsigned number) {
number &= ~1; // ~1 = 0xfffffffe (32-bit CPU)
unsigned result = 0;
while (number) {
result += number;
number -= 2;
}
return result;
}
现在,让我们玩编译器游戏并尝试手动编译它。我假设您使用的是 x86,因为这是大多数台式计算机使用的。 (x86 是 Intel compatible CPUs 的指令集。)
让我们通过一个简单的(未优化的)版本来了解这个例程在编译时的样子:
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)
现在,如您所见,代码中的常量(0
、2
、1
)实际上显示为 CPU 指令的一部分!事实上,1
根本没有出现;编译器(在这种情况下,只有我)已经计算出 ~1
并在代码中使用结果。
虽然您可以获取 CPU 指令的地址,但获取其中一部分的地址通常没有任何意义(在 x86 中有时可以,但在许多其他 CPU s 你根本不能这样做),代码地址与数据地址根本不同(这就是为什么你不能将函数指针(代码地址)视为常规指针(数据地址))。在某些 CPU 架构中,代码地址和数据地址是完全不兼容的(尽管大多数现代操作系统使用 x86 的方式并非如此)。
请注意 while (number)
等同于 while (number != 0)
。 0
根本没有出现在编译代码中!它由 jnz
指令暗示(如果不为零则跳转)。这是你不能获取那个 0
地址的另一个原因——它没有地址,它实际上无处可寻。
我希望这能让你更清楚。
where are these expressions stored such that there addresses can't be retrieved?
你的问题格式不正确。
概念上
这就像问为什么人们可以讨论名词而不是动词的所有权。名词指的是可能(可能)拥有的 事物,动词指的是执行的 动作。你不能拥有一个动作或执行一件事。
在语言规范方面
表达式首先不是存储,而是求值。
它们可能由编译器在编译时求值,或者它们可能由处理器在 运行 时间求值。
在语言实现方面
考虑声明
int a = 0;
它做了两件事:首先,它声明了一个整型变量a
。这是定义 可以获取其地址的东西。由编译器决定在给定平台上做任何有意义的事情,允许你获取a
.
的地址
其次,它将该变量的值设置为零。这确实 而不是 意味着在您编译的程序中某处存在值为零的整数。它通常可以实现为
xor eax,eax
也就是说,异或(异或)eax
寄存器本身。这总是导致零,无论之前是什么。但是,编译代码中没有固定值 0
的对象来匹配您在源代码中编写的整数文字 0
。
顺便说一句,当我说上面的 a
是您可以获取其地址的东西时 - 值得指出的是它可能实际上没有地址 除非 你拿着吧。例如,该示例中使用的 eax
寄存器没有地址。如果编译器可以证明程序仍然是正确的,a
就可以在那个寄存器中度过它的一生,而永远不会存在于主存中。相反,如果你在某处使用表达式 &a
,编译器会创建一些可寻址的 space 来存储 a
的值。
请注意比较,我可以轻松地选择不同的语言,可以获取表达式的地址。
它可能会被解释,因为一旦机器可执行输出替换了这些结构,编译通常会丢弃它们。例如 Python 有 运行 时间自省和 code
个对象。
或者我可以从 LISP 开始并扩展它以提供某种对 S 表达式的 addressof 操作。
它们的关键共同点是它们不是 C,作为设计和定义的问题不提供 那些机制。
Where are expressions and constants stored if not in memory
在某些(实际上很多)情况下,常量表达式根本不存储。特别是,想想 optimizing compilers, and see CppCon 2017: Matt Godbolt's talk “我的编译器最近为我做了什么?打开编译器的盖子”
在某些 C 代码具有 2 + 3
的特定情况下,大多数优化编译器将具有 constant folded that into 5, and that 5 constant might be just inside some machine code instruction (as some bitfield) of your code segment and not even have a well defined memory location. If that constant 5 was a loop limit, some compilers could have done loop unrolling,并且该常量将不再出现在二进制代码中。
另见 this 回答等...
注意 C11 is a specification written in English. Read its n1570 标准。另请阅读更大的 C++11(或更高版本)规范。
C(和 C++)的 semantics 禁止获取常量的地址。
来自 C 编程语言 作者:Brian W. Kernighan
& operator only applies to objects in memory: variables and array elements. It cannot be applied to expressions, constants or register variables.
如果不在内存中,表达式和常量存储在哪里? 那句话是什么意思?
例如:
&(2 + 3)
为什么我们不能获取它的地址?它存储在哪里?
C++ 的答案是否也一样,因为 C 是它的父级?
这链接 question explains 这样的表达式是 rvalue
个对象,所有 rvalue
个对象都没有地址。
我的问题是这些表达式存储在哪里,以至于无法检索它们的地址?
将地址取到表达式中并没有多大意义。您可以做的最接近的事情是函数指针。表达式的存储方式与变量和对象不同。
表达式存储在实际的机器代码中。当然,您可以找到计算表达式的地址,但这样做是没有意义的。
阅读一些关于汇编的内容。表达式存储在文本段中,而变量存储在其他段中,例如数据或堆栈。
https://en.wikipedia.org/wiki/Data_segment
另一种解释是,表达式是cpu指令,而变量是纯数据。
还有一件事需要考虑:编译器经常优化掉一些东西。考虑这段代码:
int x=0;
while(x<10)
x+=1;
此代码可能会优化为:
int x=10;
那么在这种情况下 (x+=1)
的地址是什么意思?它甚至不存在于机器代码中,因此根据定义,它根本没有地址。
这样的表达式最终成为机器代码的一部分。表达式 2 + 3
可能会被翻译成机器码指令 "load 5 into register A"。 CPU 寄存器没有地址。
考虑以下函数:
unsigned sum_evens (unsigned number) {
number &= ~1; // ~1 = 0xfffffffe (32-bit CPU)
unsigned result = 0;
while (number) {
result += number;
number -= 2;
}
return result;
}
现在,让我们玩编译器游戏并尝试手动编译它。我假设您使用的是 x86,因为这是大多数台式计算机使用的。 (x86 是 Intel compatible CPUs 的指令集。)
让我们通过一个简单的(未优化的)版本来了解这个例程在编译时的样子:
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)
现在,如您所见,代码中的常量(0
、2
、1
)实际上显示为 CPU 指令的一部分!事实上,1
根本没有出现;编译器(在这种情况下,只有我)已经计算出 ~1
并在代码中使用结果。
虽然您可以获取 CPU 指令的地址,但获取其中一部分的地址通常没有任何意义(在 x86 中有时可以,但在许多其他 CPU s 你根本不能这样做),代码地址与数据地址根本不同(这就是为什么你不能将函数指针(代码地址)视为常规指针(数据地址))。在某些 CPU 架构中,代码地址和数据地址是完全不兼容的(尽管大多数现代操作系统使用 x86 的方式并非如此)。
请注意 while (number)
等同于 while (number != 0)
。 0
根本没有出现在编译代码中!它由 jnz
指令暗示(如果不为零则跳转)。这是你不能获取那个 0
地址的另一个原因——它没有地址,它实际上无处可寻。
我希望这能让你更清楚。
where are these expressions stored such that there addresses can't be retrieved?
你的问题格式不正确。
概念上
这就像问为什么人们可以讨论名词而不是动词的所有权。名词指的是可能(可能)拥有的 事物,动词指的是执行的 动作。你不能拥有一个动作或执行一件事。
在语言规范方面
表达式首先不是存储,而是求值。 它们可能由编译器在编译时求值,或者它们可能由处理器在 运行 时间求值。
在语言实现方面
考虑声明
int a = 0;
它做了两件事:首先,它声明了一个整型变量
的地址a
。这是定义 可以获取其地址的东西。由编译器决定在给定平台上做任何有意义的事情,允许你获取a
.其次,它将该变量的值设置为零。这确实 而不是 意味着在您编译的程序中某处存在值为零的整数。它通常可以实现为
xor eax,eax
也就是说,异或(异或)
eax
寄存器本身。这总是导致零,无论之前是什么。但是,编译代码中没有固定值0
的对象来匹配您在源代码中编写的整数文字0
。
顺便说一句,当我说上面的 a
是您可以获取其地址的东西时 - 值得指出的是它可能实际上没有地址 除非 你拿着吧。例如,该示例中使用的 eax
寄存器没有地址。如果编译器可以证明程序仍然是正确的,a
就可以在那个寄存器中度过它的一生,而永远不会存在于主存中。相反,如果你在某处使用表达式 &a
,编译器会创建一些可寻址的 space 来存储 a
的值。
请注意比较,我可以轻松地选择不同的语言,可以获取表达式的地址。
它可能会被解释,因为一旦机器可执行输出替换了这些结构,编译通常会丢弃它们。例如 Python 有 运行 时间自省和 code
个对象。
或者我可以从 LISP 开始并扩展它以提供某种对 S 表达式的 addressof 操作。
它们的关键共同点是它们不是 C,作为设计和定义的问题不提供 那些机制。
Where are expressions and constants stored if not in memory
在某些(实际上很多)情况下,常量表达式根本不存储。特别是,想想 optimizing compilers, and see CppCon 2017: Matt Godbolt's talk “我的编译器最近为我做了什么?打开编译器的盖子”
在某些 C 代码具有 2 + 3
的特定情况下,大多数优化编译器将具有 constant folded that into 5, and that 5 constant might be just inside some machine code instruction (as some bitfield) of your code segment and not even have a well defined memory location. If that constant 5 was a loop limit, some compilers could have done loop unrolling,并且该常量将不再出现在二进制代码中。
另见 this 回答等...
注意 C11 is a specification written in English. Read its n1570 标准。另请阅读更大的 C++11(或更高版本)规范。
C(和 C++)的 semantics 禁止获取常量的地址。