编译器如何将内存地址分配给变量?

德文·汤克(Dervin Thunk)

我教的一门课程中,学生可以提出有关编程的问题(!):我遇到了以下问题:

为什么机器选择将变量存储在内存中?我们可以告诉它在哪里存储变量吗?

我真的不知道该说些什么。这是我的第一次尝试:

编译器(而不是机器)自动选择将变量存储在进程地址空间中的位置。使用C,我们无法告诉机器将变量存储在何处。

但这“自动地”有点滑稽可笑,并引出了一个问题……我意识到我什至不知道是编译器,运行时还是操作系统,还是由谁来执行任务。也许有人可以比我更好地回答学生的问题。

涡流

这个问题的答案非常复杂,因为根据变量范围,大小和编程环境的不同,内存分配有多种方法。

堆栈分配的变量

通常local variables放在“堆栈”上。这意味着编译器会为“堆栈指针”分配一个偏移量,该偏移量可以根据当前函数的调用而有所不同。也就是说,编译器假设程序可以访问和使用诸如Stack-Pointer + 4,Stack-Pointer + 8等的内存位置。return从函数-ing的存储器位置并不能保证保留这些值。

这被映射为类似于以下内容的汇编指令。esp是堆栈指针,esp + N指的是相对于esp的内存位置:

mov eax, DWORD PTR SS:[esp]
mov eax, DWORD PTR SS:[esp + 4]
mov eax, DWORD PTR SS:[esp + 8]

然后是堆分配的变量。这意味着有一个库调用来从标准库(alloc在C或newC ++中)请求内存保留该存储器,直到程序执行结束。allocnew在内存区域(称为堆)中返回指向内存的指针。分配函数必须确保未保留内存,这有时会使堆分配变慢。另外,如果您不希望内存不足,则应该free(或delete)不再使用的内存。堆分配在内部非常复杂,因为标准库必须跟踪内存中已使用和未使用的范围以及已释放的内存范围。因此,即使释放堆分配的变量也比分配它更耗时。有关更多信息,请参见如何在内部实现malloc()?

了解堆栈和堆之间的区别是学习如何使用C和C ++进行编程的基础。

任意指针

天真地假设,通过将指针设置为任意地址int *a = 0x123,应该可以对计算机内存中的任意位置进行寻址。这并不完全成立,因为在寻址内存时(取决于CPU和系统)程序受到严格限制。

感受记忆

在有指导的课堂经验中,通过将源代码编译为汇编器来探索一些简单的C代码可能是有益的(例如,gcc可以做到这一点)。一个简单的函数int foo(int a, int b) { return a+b;}就足够了(没有优化)。然后看到类似的东西int bar(int *a, int *b) { return (*a) + (*b);}

调用bar时,在堆栈上分配一次参数,每个malloc分配一次。

结论

编译器的确执行一些相对于基本地址的可变放置和对齐,这些基本地址是由程序/标准库在运行时获得的。

要深入了解与内存相关的问题,请参见Ulrich Drepper的“每个程序员应了解的内存知识” http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.91.957

除了C-ish Country idenote

然后还有Garbage Collection,它在许多脚本语言(Python,Perl,Javascript,lisp)和独立于设备的环境(Java,C#)中很流行。它与堆分配有关,但稍微复杂一些。

各种编程语言仅基于堆(无堆栈python)或完全基于堆(第四)。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章