如何在C中的内存中存储variables名?
在C中,假设你有一个名为variable_name
。 假设它位于0xaaaaaaaa
,并且在那个内存地址处,您有整数123.换句话说, variable_name
包含123。
我正在寻找澄清措辞“ variable_name
位于0xaaaaaaaa
”。 编译器如何识别string“variable_name”与该特定内存地址相关联? string“variable_name”是否存储在内存的某个地方? 编译器是否只是在它看到它时将variable_name
replace为0xaaaaaaaa
,如果是的话,是不是必须使用内存才能进行replace?
编译器运行后,variables名不再存在(禁止像共享库或debugging符号中的导出全局variables这样的特殊情况)。 整个编译行为的目的是把你的源代码所代表的象征性名称和algorithm变成本地机器指令。 所以是的,如果你有一个全局variable_name
,编译器和链接器决定把它放在0xaaaaaaaa
,那么无论它在代码中使用什么地方,它只会通过该地址访问。
所以要回答你的字面问题:
编译器如何识别string“variable_name”与该特定内存地址相关联?
工具链(编译器和链接器)一起工作,为variables分配一个内存位置。 跟踪所有的引用是编译器的工作,而链接器将在稍后提供正确的地址。
string
"variable_name"
存储在内存的某个地方?
只有编译器正在运行。
编译器是否只是在它看到它时将
variable_name
replace为0xaaaaaaaa
,如果是的话,是不是必须使用内存才能进行replace?
是的,这几乎是发生了什么,除了这是连接器的两个阶段的工作。 是的,它使用内存,但它是编译器的内存,而不是在程序的运行时。
一个例子可能会帮助你理解。 让我们试试这个程序:
int x = 12; int main(void) { return x; }
很简单,对吗? 好。 让我们把这个程序,并编译它,看看反汇编:
$ cc -Wall -Werror -Wextra -O3 example.c -o example $ otool -tV example example: (__TEXT,__text) section _main: 0000000100000f60 pushq %rbp 0000000100000f61 movq %rsp,%rbp 0000000100000f64 movl 0x00000096(%rip),%eax 0000000100000f6a popq %rbp 0000000100000f6b ret
看到movl
线? 它抓取全局variables(在这种情况下,相对于指令指针来说)。 再也没有提到x
。
现在让我们来更复杂一点,并添加一个局部variables:
int x = 12; int main(void) { volatile int y = 4; return x + y; }
这个程序的反汇编是:
(__TEXT,__text) section _main: 0000000100000f60 pushq %rbp 0000000100000f61 movq %rsp,%rbp 0000000100000f64 movl $0x00000004,0xfc(%rbp) 0000000100000f6b movl 0x0000008f(%rip),%eax 0000000100000f71 addl 0xfc(%rbp),%eax 0000000100000f74 popq %rbp 0000000100000f75 ret
现在有两个movl
指令和一个addl
指令。 你可以看到第一个movl
正在初始化y
,它决定了它将在栈上(基址指针-4)。 然后,下一个movl
将全局x
变成一个寄存器eax
, addl
将y
addl
给该值。 但是,正如你所看到的,文字x
和y
string不再存在。 他们对你来说是程序员的便利,但计算机在执行时并不关心他们。
AC编译器首先创build一个符号表,它存储variables名和它在内存中的位置之间的关系。 编译时,它使用这个表格来replace具有特定内存位置的variables的所有实例,正如其他人所说的那样。 你可以在维基百科页面find更多。
所有variables都被编译器替代。 首先它们被引用取代,后来链接器放置地址而不是引用。
换一种说法。 只要编译器运行完毕,variables名就不可用
这就是所谓的实现细节 。 虽然你所描述的是我曾经使用的所有编译器的情况,但并不一定如此。 AC编译器可以将每个variables放在一个散列表中,并在运行时(或类似的东西)查看它们,事实上,早期的JavaScript解释器就是这样做的(现在,它们进行Just-In-TIme编译,导致更为原始的东西)。
特别是对于像VC ++,GCC和LLVM这样的常见编译器来说:编译器通常会为内存中的某个位置分配一个variables。 全局或静态作用域的variables得到一个固定的地址,在程序运行时不会改变,而函数中的variables得到一个堆栈地址 – 也就是相对于当前堆栈指针的地址,每次函数改变调用。 (这是过于简单化)。函数返回后,堆栈地址将立即失效,但具有有效地使用零开销的好处。
一旦一个variables有一个分配给它的地址,就不再需要这个variables的名字,所以它被丢弃了。 取决于名称的种类,可以在预处理时间(对于macros名称),编译时间(对于静态和局部variables/函数)和链接时间(对于全局variables/函数)时放弃名称。如果符号被导出使其他程序可见,以便他们可以访问它),该名称通常会保留在“符号表”中,占用一定数量的内存和磁盘空间。
编译器是否只是在它看到它时将variable_namereplace为0xaaaaaaaa,
是。
是不是要用内存来做这个replace?
不,因为编译器是为你做的。 由编译器构build的程序不知道variables的名称。
只要编译器看到它,是否只是用variable_namereplace0xaaaaaaaa
是。
如果是的话,是不是要用记忆来替代呢?
是。 但它是编译器,它编译你的代码后,你为什么关心内存?