c-决定全局变量的内存地址.编译器或操作系统



考虑下面的程序。

int a = 0x45;
int main()
{
   int i = a;
   return 0;
}
;; asm code
call   0x401780 <__main>
mov    0x402000,%eax   // why does it allocate 0x402000 only for global 'a'?
mov    %eax,0xc(%esp)
mov    $0x0,%eax
leave

这是在Windows/xp上的CodeBlocks中生成的等效程序集代码。我知道0x402000是数据段地址。但那个内存位置是由编译器硬编码的吗?

我认为它不是硬编码的,因为其他应用程序可能也会使用该内存位置。

众所周知,操作系统为局部变量分配堆栈帧,并返回堆栈帧的基加数。并且使用具有偏移的CCD_ 2寄存器来访问局部变量。

操作系统对全局变量也做同样的操作吗?如果它也这样做,为什么值是硬编码的?

dw a 0x40; this directive allocates memory on data segment
mov %ax,a; copies value of a to accumulator

但编译器怎么知道"a"有内存地址0x402000呢。如果编译器已将值硬编码为0x402000,则应首先确保该地址未被其他应用程序使用,对吗?

如果操作系统在数据段上分配内存,那么内存地址应该根据应用程序和资源而变化。有人能解释一下当我定义全局变量时会发生什么吗?

正如Falken教授所提到的,这取决于编译器/系统。。。但是Linux,Windows,Mac,流行/主要工具链:

编译器获取高级源并从中进行汇编,汇编程序将其转换为对象。对象解析它可以解析的相对地址,但为链接器留下线索。

链接器。。。链接。。。它获取对象,它们的二进制Blob,将它们排列到它所知的二进制地址空间中,为全局变量和函数等选择地址。基本上,它放置.text、.data和.bss.

然后是硬件中的mmu,这让生活变得简单多了,例如,你可以编译每个地址为0x8000的程序作为入口点,并且有很多程序同时在地址0x8000运行。因为他们都认为自己在那个地址,因为在mmu虚拟侧的虚拟地址空间中。在物理方面,他们实际上都住在不同的地址,但通常只有操作系统需要关心这一点。

因此,如今的编译器通常会按照我们在对象、.data和.bss项的源代码中编写函数的顺序来放置函数,这些项有时会重新排列在我们身上。链接器通常按照要求操作,谁告诉他们?最终,我们是程序员,但提供给您的工具链有默认值(如自动将编译的代码组装到对象中并自动链接),包括引导程序代码和默认链接器脚本。该目标操作系统的编译器的默认链接器脚本是根据该操作系统的规则设置的。

以上是您通常在gcc和其他主流操作系统(windows、mac和*nix)的主要编译器中看到的内容。这并不意味着现在没有工具链可以直接编译到最终二进制文件,也不意味着汇编程序可以直接编译最终二进制文件而不是对象。当然,历史上也不总是这样。在你进入这些角落的案例之前,我认为你在深入研究这些工具时会有上述经验。

这取决于操作系统和编译器。

例如,在Amiga上,如果我没记错的话,绝对地址存储在磁盘上的可执行文件中。但当操作系统加载二进制文件时,它会动态重写地址,以适应为程序分配的内存区域。

在你的情况下,我认为在DOS"小型"内存模型程序的64k限制下,地址可以是绝对的。64k是8086体系结构中的一个段,DOS会为它加载的每个"小"内存模型程序分配一个完整的段。".COM"文件按原样加载到64k DOS段中。

我可能没有完全理解术语和细节,但我的主要观点是,这取决于所讨论的操作系统和编译器。

相关内容

最新更新