C中变量的地址是计算机RAM中的真实地址吗?
Is the address of a variable in C the real address in the RAM of the computer?
在 C 中,当您获取变量的地址时,该地址是计算机 RAM 中真正存在的地址,或者只是 C 编译器中假内存中的地址(如果它确实如此工作) ?能通俗的解释一下吗?
是也不是。当你获取一个变量的地址,并对其进行一些操作时(假设编译器没有优化它),它会对应于 ram 中的一个地址。但是由于虚拟内存,你程序中使用的地址几乎肯定不是物理内存中变量的地址。内核重新映射哪些虚拟地址(您的程序看到的)引用哪些物理地址(内存看到的),以便不同的进程可以同时加载到内存中,但不能访问彼此的内存。此外,您的进程的内存可以被调出,或者如果最近没有使用它,则可以写入磁盘 and/or 其他东西需要更多内存,并重新加载到一个完全不同的地址,但虚拟地址将保持不变。
所以是的,当您访问一个指针时,该地址对应于内存中的一个地址。但是那个地址并不对应于ram中的实际地址,它对应的地址会随着时间的推移而变化。
在 multi-process
环境中同时有多个进程 运行 的情况下,linker
无法在编译时决定变量的地址。
原因很简单,如果您为变量分配专用地址,那么您就限制了系统上可以 运行 的进程数。
因此,他们在 OS 和处理器的帮助下,将虚拟地址分配给变量,并在 run-time 期间将这些地址转换为物理地址。
这种系统的一个例子是 linux
运行ning on x86
CPU.
在处理器上只有一个 process/application 运行 的其他情况下,链接器可以将实际物理地址分配给变量。
示例:嵌入式系统执行专用任务,例如 Oven。
在古老的操作系统上,目标处理器上不存在 MMU,或者未使用(即使处理器允许)。
在那种情况下,使用物理地址,这更容易理解但也很烦人,因为当您调试汇编程序或尝试解码回溯时,您必须知道程序加载的位置或 post-mortem traceback 没用。
没有 MMU,你可以做一些非常 hacky 和简单的事情。共享内存可以用几行代码编写,你可以很容易地检查整个内存等等...
在现代操作系统上,依赖于 MMU 处理器能力和地址转换,可执行文件 运行 在虚拟内存中,这不是问题,因为它们无法访问无论如何其他可执行文件内存。
好的一面是,如果您多次 running/debugging 同一个可执行文件,您总是会得到相同的地址。在必须多次重新启动调试器的长时间调试会话中很有用。
此外,一些 languages/compilers(如 GNAT Ada 编译器)在程序执行某些非法操作时提供带地址的回溯。在可执行文件上使用 addr2line
,即使在进程结束且内存已释放后,您也可以获得准确的回溯。
我知道的例外是 Windows 共享库 (DLL),它们几乎从不加载到同一地址,因为该地址可能在多个可执行文件之间共享。例如,在这些情况下,post-mortem 回溯将毫无用处,因为声明的符号地址与实际回溯地址存在偏移量。
排序答案是“都不是”。
一般来说,变量在内存中的地址是在 运行ning 程序地址 space.
的上下文中
不同之处在于主机系统如何将程序的地址 space 映射到硬件。
对于具有内存管理单元 (MMU) 的现代硬件和使用 MMU 的操作系统(或其设备驱动程序),程序的地址 space 被映射到物理内存,其中可能包括RAM 或虚拟内存,例如硬盘驱动器上的交换文件。操作系统使用MMU来隔离程序彼此(因此两个进程不能访问对方的地址space),也使用MMU来支持RAM和swap之间的数据交换。 运行ning 进程通常无法判断其数据在物理内存中的位置,因为操作系统和 MMU 特别阻止它这样做。随着时间的推移,操作系统和 MMU 可能会将程序使用的内存迁移到 RAM 的不同区域或交换,但程序无法检测到这一点,因为操作系统和 MMU 负责映射程序中的地址(它永远不会随着就程序而言)到实际地址。这涵盖了 windows、unix 和各种实时操作系统的大多数现代版本。 (这些系统通常还提供以编程方式访问物理内存的方法,但仅适用于 运行 更高权限的程序或内核模式驱动程序)。
较旧的硬件没有 MMU,因此操作系统无法为程序提供单独的地址 space。在这样的系统上,程序看到的地址与物理内存中的某个位置有 one-to-one 对应。
介于两者之间的是具有独立物理内存区域的硬件(例如,由不同的内存芯片组提供)。在这些系统上,在特殊驱动程序的支持下,主机系统可以在程序地址 space 中的地址与物理内存特定区域中的位置之间实现部分映射。这就是为什么一些目标系统和支持它们的编译器支持不止一种指针类型(例如,名称如 near、far 和 huge)作为编译器扩展。在那些情况下,指针可以指向特定内存区域中的位置,并且对于每种指针类型,从程序看到的指针值到相应区域中的实际位置可能存在一些值映射物理内存。
C 编译器不会成为它构建的可执行程序的一部分(否则,要安装任何构建的程序,还需要安装和执行用于构建它的编译器,否则程序不会 运行).通常,编译器在执行程序时不再 运行ning(或者,至少,程序不能依赖它的存在)。因此,程序无法访问编译器地址 space.
内的地址
在解释环境中(例如 C 代码由另一个程序——解释器解释),解释器充当程序和硬件之间的中介,并处理程序地址 space、解释器地址之间的映射地址 space 和物理内存。与使用编译器和链接器的工具链相比,C 解释器在实践中相对较少。
在 C 中,当您获取变量的地址时,该地址是计算机 RAM 中真正存在的地址,或者只是 C 编译器中假内存中的地址(如果它确实如此工作) ?能通俗的解释一下吗?
是也不是。当你获取一个变量的地址,并对其进行一些操作时(假设编译器没有优化它),它会对应于 ram 中的一个地址。但是由于虚拟内存,你程序中使用的地址几乎肯定不是物理内存中变量的地址。内核重新映射哪些虚拟地址(您的程序看到的)引用哪些物理地址(内存看到的),以便不同的进程可以同时加载到内存中,但不能访问彼此的内存。此外,您的进程的内存可以被调出,或者如果最近没有使用它,则可以写入磁盘 and/or 其他东西需要更多内存,并重新加载到一个完全不同的地址,但虚拟地址将保持不变。
所以是的,当您访问一个指针时,该地址对应于内存中的一个地址。但是那个地址并不对应于ram中的实际地址,它对应的地址会随着时间的推移而变化。
在 multi-process
环境中同时有多个进程 运行 的情况下,linker
无法在编译时决定变量的地址。
原因很简单,如果您为变量分配专用地址,那么您就限制了系统上可以 运行 的进程数。
因此,他们在 OS 和处理器的帮助下,将虚拟地址分配给变量,并在 run-time 期间将这些地址转换为物理地址。
这种系统的一个例子是 linux
运行ning on x86
CPU.
在处理器上只有一个 process/application 运行 的其他情况下,链接器可以将实际物理地址分配给变量。 示例:嵌入式系统执行专用任务,例如 Oven。
在古老的操作系统上,目标处理器上不存在 MMU,或者未使用(即使处理器允许)。
在那种情况下,使用物理地址,这更容易理解但也很烦人,因为当您调试汇编程序或尝试解码回溯时,您必须知道程序加载的位置或 post-mortem traceback 没用。
没有 MMU,你可以做一些非常 hacky 和简单的事情。共享内存可以用几行代码编写,你可以很容易地检查整个内存等等...
在现代操作系统上,依赖于 MMU 处理器能力和地址转换,可执行文件 运行 在虚拟内存中,这不是问题,因为它们无法访问无论如何其他可执行文件内存。
好的一面是,如果您多次 running/debugging 同一个可执行文件,您总是会得到相同的地址。在必须多次重新启动调试器的长时间调试会话中很有用。
此外,一些 languages/compilers(如 GNAT Ada 编译器)在程序执行某些非法操作时提供带地址的回溯。在可执行文件上使用 addr2line
,即使在进程结束且内存已释放后,您也可以获得准确的回溯。
我知道的例外是 Windows 共享库 (DLL),它们几乎从不加载到同一地址,因为该地址可能在多个可执行文件之间共享。例如,在这些情况下,post-mortem 回溯将毫无用处,因为声明的符号地址与实际回溯地址存在偏移量。
排序答案是“都不是”。
一般来说,变量在内存中的地址是在 运行ning 程序地址 space.
的上下文中不同之处在于主机系统如何将程序的地址 space 映射到硬件。
对于具有内存管理单元 (MMU) 的现代硬件和使用 MMU 的操作系统(或其设备驱动程序),程序的地址 space 被映射到物理内存,其中可能包括RAM 或虚拟内存,例如硬盘驱动器上的交换文件。操作系统使用MMU来隔离程序彼此(因此两个进程不能访问对方的地址space),也使用MMU来支持RAM和swap之间的数据交换。 运行ning 进程通常无法判断其数据在物理内存中的位置,因为操作系统和 MMU 特别阻止它这样做。随着时间的推移,操作系统和 MMU 可能会将程序使用的内存迁移到 RAM 的不同区域或交换,但程序无法检测到这一点,因为操作系统和 MMU 负责映射程序中的地址(它永远不会随着就程序而言)到实际地址。这涵盖了 windows、unix 和各种实时操作系统的大多数现代版本。 (这些系统通常还提供以编程方式访问物理内存的方法,但仅适用于 运行 更高权限的程序或内核模式驱动程序)。
较旧的硬件没有 MMU,因此操作系统无法为程序提供单独的地址 space。在这样的系统上,程序看到的地址与物理内存中的某个位置有 one-to-one 对应。
介于两者之间的是具有独立物理内存区域的硬件(例如,由不同的内存芯片组提供)。在这些系统上,在特殊驱动程序的支持下,主机系统可以在程序地址 space 中的地址与物理内存特定区域中的位置之间实现部分映射。这就是为什么一些目标系统和支持它们的编译器支持不止一种指针类型(例如,名称如 near、far 和 huge)作为编译器扩展。在那些情况下,指针可以指向特定内存区域中的位置,并且对于每种指针类型,从程序看到的指针值到相应区域中的实际位置可能存在一些值映射物理内存。
C 编译器不会成为它构建的可执行程序的一部分(否则,要安装任何构建的程序,还需要安装和执行用于构建它的编译器,否则程序不会 运行).通常,编译器在执行程序时不再 运行ning(或者,至少,程序不能依赖它的存在)。因此,程序无法访问编译器地址 space.
内的地址在解释环境中(例如 C 代码由另一个程序——解释器解释),解释器充当程序和硬件之间的中介,并处理程序地址 space、解释器地址之间的映射地址 space 和物理内存。与使用编译器和链接器的工具链相比,C 解释器在实践中相对较少。