C中变量的地址是计算机RAM中的实际地址吗?

时间:2018-02-25 02:21:17

标签: c memory memory-address

在C中,当你得到一个变量的地址时,该地址确实存在于计算机的RAM中,或者只是C编译器中假存储器中的地址(如果这是真的有效)?你能用外行的话来解释吗?

4 个答案:

答案 0 :(得分:2)

是和否。当你获取变量的地址并对其执行某些操作时(假设编译器没有对其进行优化),它将对应于ram中的地址。但是由于虚拟内存,程序中使用的地址几乎肯定不是物理内存变量的地址。内核重新​​映射虚拟地址(程序看到的内容)指的是哪些物理地址(内存看到的内容),以便不同的进程可以同时加载到内存中,但无法访问彼此。记忆。此外,您的进程内存可以被分页,或者如果最近没有使用过和/或其他需要更多内存的话,可以写入磁盘,并重新加载到完全不同的地址,但虚拟地址将保持不变。

所以是的,当你访问指针时,该地址对应于内存中的地址。但是该地址与ram中的实际地址不对应,并且它对应的地址可能随时间而变化。

答案 1 :(得分:1)

排序答案是"不是"。

一般而言,内存中变量的地址是在正在运行的程序的地址空间的上下文中。

主机系统将程序的地址空间映射到硬件的方式不同。

使用具有内存管理单元(MMU)的现代硬件和使用MMU的操作系统(或其设备驱动程序),程序的地址空间被映射到物理内存,物理内存可能包含RAM或虚拟内存,例如硬盘上的交换文件。操作系统使用MMU将程序彼此隔离(因此两个进程无法访问彼此的地址空间),并且还使用MMU来支持RAM和交换之间的数据交换。运行过程通常无法分辨其数据在物理内存中的位置,因为操作系统和MMU特别阻止它这样做。随着时间的推移,操作系统和MMU可能会将使用程序的内存迁移到RAM的不同区域或交换,但程序无法检测到这一点,因为操作系统和MMU负责映射程序中的地址(它永远不会改变为就程序而言)到实际地址。这涵盖了大多数现代版本的windows,unix和各种实时操作系统。 (这些系统通常还提供以编程方式访问物理内存的方法,但仅适用于以较高权限运行的程序或内核模式驱动程序)。

较旧的硬件没有MMU,因此操作系统无法为程序提供单独的地址空间。在这样的系统上,程序看到的地址与物理内存中的位置一一对应。

其中介于两者之间的是具有单独物理存储区域的硬件(例如,由不同的存储芯片组提供)。在这些系统上,在特殊驱动程序的支持下,主机系统可以在程序的地址空间中的地址与物理存储器的特定区域中的位置之间实现部分映射。这就是为什么一些目标系统和支持它们的编译器支持多种指针类型(例如,使用near,far和huge等名称)作为编译器扩展。在这些情况下,指针可以指代存储器的特定区域中的位置,并且对于每个指针类型,可以存在从程序看到的指针的值到相应区域内的实际位置的值的映射。物理记忆。

C编译器不会成为它构建的可执行程序的一部分(否则,要安装任何构建的程序,还需要安装和执行用于构建它的编译器,否则程序将无法运行)。通常,在执行程序时(或者至少程序不能依赖它存在),编译器不再运行。因此,程序不能访问编译器地址空间内的地址。

在解释环境中(例如,C代码由另一个程序 - 解释器解释),解释器充当程序和硬件之间的中介,并处理程序的地址空间,解释器和#39之间的映射。 ; s地址空间和物理内存。与使用编译器和链接器的工具链相比,C语言解释器在实践中相对较少。

答案 2 :(得分:0)

如果@echo off setlocal EnableExtensions EnableDelayedExpansion set "File=testdoc.txt" set "_Temp=%TEMP%\%~n0.tmp" set "TaskNumber=%~1" findstr /B /L /M /C:"%TaskNumber%:" "%File%" >nul 2>&1 if errorlevel 1 echo ERROR: %TaskNumber% not found in file "%File%" & goto:eof set "var=false" if /I "%~2" == "enable" set "var=true" if "%~2" == "" set "var=true" if "%var%" == "true" ( del "%_Temp%" 2>nul for /F "usebackq tokens=1* delims=:" %%I in ("%File%") do ( if not "%%I" == "%TaskNumber%" ( >>"%_Temp%" echo %%I:%%J ) else ( set "TextLine=%%I:%%J" if not "!TextLine:~-2!" == ":N" ( >>"%_Temp%" echo !TextLine!:N ) ) ) ) else ( if /I "%~2" == "disable" ( del "%_Temp%" 2>nul for /F "usebackq tokens=1* delims=:" %%I in ("%File%") do ( if not "%%I" == "%TaskNumber%" ( >>"%_Temp%" echo %%I:%%J ) else ( set "TextLine=%%I:%%J" if "!TextLine:~-2!" == ":N" ( >>"%_Temp%" echo !TextLine:~0,-2! ) ) ) ) ) move /Y "%TempFile%" "%TextFile%" 2>nul 环境中多个进程同时运行,multi-process无法在编译时决定变量的地址。 原因很简单,如果为变量分配专用地址,则限制可以在系统上运行的进程数。

因此,他们在操作系统和处理器的帮助下,为变量分配了一个虚拟地址,并将这些地址转换为物理地址。 此类系统的一个示例是linkerlinux CPU上运行。

在处理器上只运行一个进程/应用程序的其他情况下,链接器可以为变量分配实际的物理地址。 示例:执行专用任务的嵌入式系统,例如Oven。

答案 3 :(得分:0)

在古老的操作系统上,MMU不会出现在目标处理器上,或者没有使用(即使处理器允许)。

在这种情况下,使用物理地址,这更容易理解但也很烦人,因为当您调试汇编程序或尝试解码回溯时,您必须知道程序的加载位置或帖子-mortem traceback是没用的。

如果没有MMU,你可以做非常hacky&简单的事情。共享内存可以编写几行,您可以非常容易地检查整个内存等...

在现代操作系统上,依赖于MMU处理器功能和地址转换,可执行文件在虚拟内存中运行,这不是问题,因为它们无论如何都无法访问其他可执行文件内存。

好的一面是,如果您多次运行/调试相同的可执行文件,则始终会获得相同的地址。在长调试会话中很有用,您必须多次重启调试器。

此外,某些语言/编译器(如GNAT Ada编译器)在程序执行非法操作时提供带地址的回溯。在可执行文件上使用addr2line,即使在进程结束并释放内存后,您也能够获得准确的回溯。

我所知道的异常是Windows共享库(DLL),它几乎从不在同一地址加载,因为这个地址可能在几个可执行文件之间共享。例如,在这些情况下,事后追溯将毫无用处,因为声明的符号地址与实际的追溯地址有偏差。