C++ 中的 constexpr 指针和内存管理
constexpr pointers and memory management in C++
引用自 C++ Primer:
The address of an object defined outside of any function is a constant expression, and so may be used to initialize a constexpr pointer.
其实我每次编译运行下面的一段代码:
#include <iostream>
using namespace std;
int a = 1;
int main()
{
constexpr int *p = &a;
cout << "p = " << p << endl;
}
我总是得到输出:
p = 0x601060
现在,这怎么可能?如何在编译时知道对象(全局或非全局)的地址并将其分配给 constexpr?如果程序执行时那部分内存被用于其他用途怎么办?
我一直假设内存是经过管理的,以便在执行程序时分配空闲部分,但内存的哪个特定部分并不重要。然而,由于这里我们有一个 constexpr 指针,程序总是需要一个特定的部分,它必须是自由的以允许程序执行。这对我来说没有意义,有人可以解释一下这种行为吗?谢谢
编辑:在阅读了您的答案和几篇在线文章后,我意识到我错过了虚拟内存的整个概念……现在它是有道理的。令人惊讶的是,C++ Primer 和 Accelerated C++ 都没有提到这个概念(也许他们会在后面的章节中提到,我还在阅读.. .).
不过,再次引用 C++ Primer:
A constant expression is an expression whose value cannot change and that can be evaluated at compile time.
鉴于链接器在计算全局对象的固定地址方面起着重要作用,如果书中说 "constant expression can be evaluated at link time" 而不是 "at compile time"。
会更准确
之所以有效,是因为全局变量在静态存储中。
这是因为 global/static 变量的 space 是在编译时在编译器生成的二进制文件中分配的,在程序机器代码旁边的区域中称为 "data"分割。当二进制文件被复制并加载到内存中时,数据段变为可读写。
这篇维基百科文章包含一个很好的图表,说明 "data" 段在虚拟地址中的位置 space:
https://en.wikipedia.org/wiki/Data_segment
自动变量不存储在数据段中,因为它们的实例化次数可能与其父函数被调用的次数一样多。此外,它们可以分配在堆栈的任何深度。因此一般情况下在编译时不可能知道自动变量的地址。
全局变量不是这种情况,它们在程序的整个生命周期中显然是唯一的。这允许编译器为独立于堆栈的变量分配一个固定地址。
在编译时就知道对象的地址实际上是不正确的。在编译时已知的是 offset。当程序被编译时,地址并没有被发射到目标文件中,而是一个标记来指示偏移量和部分。
为了简单起见,链接器随后出现,测量每个部分的大小,将它们拼接在一起并计算每个目标文件中每个标记的地址,因为它有一个具体的 'base address'每个部分。
当然没那么简单。链接器还可以在其输出中发出所有这些调整值的位置映射,以便加载程序或加载时链接器可以在 运行 时间之前重新调整它们。
从逻辑上讲,从所有意图和目的来看,地址 是 一个常量 从程序的角度来看 。只是直到 link/load 时间才给常量赋值。当该值可用时,对该常量的每个引用都会被 linker/loader.
覆盖
如果您的问题是 "why is it always the same address?",那是因为您的 OS 使用了在虚拟内存管理器之上分层的标准虚拟内存布局。进程中的地址不是真正的内存地址——它们是逻辑内存地址。 'address' 处的硅片由虚拟内存管理电路映射。因此每个进程都可以使用 "same" 地址,而实际上使用的是内存芯片的不同区域。
我可以继续讨论分页内存的进出,这是相关的,但这是一个很长的话题。鼓励进一步阅读。
引用自 C++ Primer:
The address of an object defined outside of any function is a constant expression, and so may be used to initialize a constexpr pointer.
其实我每次编译运行下面的一段代码:
#include <iostream>
using namespace std;
int a = 1;
int main()
{
constexpr int *p = &a;
cout << "p = " << p << endl;
}
我总是得到输出:
p = 0x601060
现在,这怎么可能?如何在编译时知道对象(全局或非全局)的地址并将其分配给 constexpr?如果程序执行时那部分内存被用于其他用途怎么办?
我一直假设内存是经过管理的,以便在执行程序时分配空闲部分,但内存的哪个特定部分并不重要。然而,由于这里我们有一个 constexpr 指针,程序总是需要一个特定的部分,它必须是自由的以允许程序执行。这对我来说没有意义,有人可以解释一下这种行为吗?谢谢
编辑:在阅读了您的答案和几篇在线文章后,我意识到我错过了虚拟内存的整个概念……现在它是有道理的。令人惊讶的是,C++ Primer 和 Accelerated C++ 都没有提到这个概念(也许他们会在后面的章节中提到,我还在阅读.. .). 不过,再次引用 C++ Primer:
A constant expression is an expression whose value cannot change and that can be evaluated at compile time.
鉴于链接器在计算全局对象的固定地址方面起着重要作用,如果书中说 "constant expression can be evaluated at link time" 而不是 "at compile time"。
会更准确之所以有效,是因为全局变量在静态存储中。
这是因为 global/static 变量的 space 是在编译时在编译器生成的二进制文件中分配的,在程序机器代码旁边的区域中称为 "data"分割。当二进制文件被复制并加载到内存中时,数据段变为可读写。
这篇维基百科文章包含一个很好的图表,说明 "data" 段在虚拟地址中的位置 space:
https://en.wikipedia.org/wiki/Data_segment
自动变量不存储在数据段中,因为它们的实例化次数可能与其父函数被调用的次数一样多。此外,它们可以分配在堆栈的任何深度。因此一般情况下在编译时不可能知道自动变量的地址。
全局变量不是这种情况,它们在程序的整个生命周期中显然是唯一的。这允许编译器为独立于堆栈的变量分配一个固定地址。
在编译时就知道对象的地址实际上是不正确的。在编译时已知的是 offset。当程序被编译时,地址并没有被发射到目标文件中,而是一个标记来指示偏移量和部分。
为了简单起见,链接器随后出现,测量每个部分的大小,将它们拼接在一起并计算每个目标文件中每个标记的地址,因为它有一个具体的 'base address'每个部分。
当然没那么简单。链接器还可以在其输出中发出所有这些调整值的位置映射,以便加载程序或加载时链接器可以在 运行 时间之前重新调整它们。
从逻辑上讲,从所有意图和目的来看,地址 是 一个常量 从程序的角度来看 。只是直到 link/load 时间才给常量赋值。当该值可用时,对该常量的每个引用都会被 linker/loader.
覆盖如果您的问题是 "why is it always the same address?",那是因为您的 OS 使用了在虚拟内存管理器之上分层的标准虚拟内存布局。进程中的地址不是真正的内存地址——它们是逻辑内存地址。 'address' 处的硅片由虚拟内存管理电路映射。因此每个进程都可以使用 "same" 地址,而实际上使用的是内存芯片的不同区域。
我可以继续讨论分页内存的进出,这是相关的,但这是一个很长的话题。鼓励进一步阅读。