编译器运行后,变量名将不再存在(除了共享库或调试符号中的出口全局变量这样的特殊情况)。编译的整个过程旨在将源代码中表示的符号名称和算法转换为本机机器指令。因此,如果您有一个全局 variable_name
,而编译器和链接器决定将其放置在 0xaaaaaaaa
,那么无论它在代码中的使用位置如何,都将通过该地址进行访问。
因此,回答您的字面问题:
编译器如何识别字符串“variable_name”与特定内存地址相关联?
工具链(编译器和链接器)一起分配变量的内存位置。编译器的工作是跟踪所有引用,而链接器稍后会放入正确的地址。
字符串"variable_name"
是否存储在内存中?
只有在编译器运行期间才存储。
编译器是否只是在看到variable_name
时将其替换为0xaaaaaaaa
,如果是,那么它不必使用内存来进行替换吗?
是的,这几乎就是发生的情况,但这是一个由链接器完成的两个阶段的工作。是的,它使用内存,但它是编译器的内存,而不是程序运行时的任何东西。
一个例子可能有助于您理解。让我们尝试运行此程序:
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
了吗?它正在以指令指针相对的方式获取全局变量(在本例中)。不再提到x
。
现在让我们让它变得更加复杂,添加一个局部变量:
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
添加到该值中。但是如您所见,文本字面意义上的x
和y
字符串不再存在了。它们只是为了方便程序员而存在,但计算机在执行时并不关心它们。