编译器如何实现变量的范围?我的意思是,当我们说静态变量时,范围仅限于在定义静态变量的同一文件中定义的块或函数?这是如何在机器级别或内存级别实现的?
这个限制实际上是如何实现的?
在程序运行时如何解决这个范围?
在机器级别根本无法实现。编译器在实际生成机器代码之前检查范围。C 的规则是由编译器实现的,而不是由机器实现的。编译器必须检查这些规则,机器不会也不能。
关于编译器如何检查的一个非常简单的解释:
每当引入范围时,编译器都会给它一个名称并将其放入一个结构(树)中,这样可以轻松确定该范围相对于其他范围的位置,并将其标记为当前范围。声明变量时,将其分配给当前范围。访问变量时,会在当前范围内查找。如果未找到,则查找树以查找当前树之上的范围。这一直持续到我们到达最高范围。如果仍然没有找到变量,那么我们就有了范围冲突。
在编译器内部,定义了它的实现。例如,如果我正在编写编译器,我会使用树来定义“范围”,它肯定是二叉树中的符号表。
有些人会使用任意深度的哈希表。它的所有实现都定义了。
我不是 100% 确定我理解你在问什么,但如果你的意思是“静态变量和函数如何存储在最终程序中”,那是实现定义的。
也就是说,存储此类变量和函数的常用方法与任何其他全局符号(以及一些非全局符号)位于相同的位置——不同之处在于这些不是“导出”的,因此在任何外部都不可见代码试图链接到我们的软件。
换句话说,一个包含以下内容的程序:
int var;
static int svar;
int func() { static int func_static; ... }
static int sfunc() { ... }
...在内存中可能有以下布局(假设我们的数据从 开始,0xF000
函数在0xFF00
):
0xF000: var
0xF004: svar
0xF008: func.func_static
...
0xFF00: func's data
0xFF40: sfunc's data /* assuming we needed 0x40 bytes for `func`! */
但是,导出列表将仅包含非静态符号,也就是导出的符号:
var v 0xF000
func f 0xFF00
再次——注意,虽然静态数据仍被写入文件(它必须存储在某个地方!),但它不会被导出;通俗地说,我们的程序不会告诉任何人它包含svar
,sfunc
和类似的。
nm
在 Unices 中,您可以使用该工具列出库或程序导出的符号: http ://unixhelp.ed.ac.uk/CGI/man-cgi?nm ;确实存在适用于 Windows 的类似工具(GnuWin32 可能有类似的东西)。
在实践中,可执行代码通常与数据分开存储(例如,这样可以防止写入),并且它们都可以重新排序以最大限度地减少内存使用和缓存未命中,但想法保持不变。
当然,可以应用优化——例如,可以在每次调用中内联静态函数,这意味着根本不会为函数本身生成任何代码,因此它不会在任何地方单独存在。