编译器运行后变量名不再存在(除非在共享库中导出全局变量或调试符号等特殊情况)。整个编译行为的目的是获取源代码所代表的那些符号名称和算法,并将它们转换为本地机器指令。所以是的,如果你有一个全局的variable_name,并且编译器和链接器决定把它放在0xaaaaaaaa,那么无论它在代码中使用什么,它都会通过那个地址访问。
所以回答你的字面问题:
编译器如何识别字符串“variable_name”与该特定内存地址相关联?
工具链(编译器和链接器)共同为变量分配内存位置。跟踪所有引用是编译器的工作,链接器稍后会放入正确的地址。
字符串"variable_name" 是否存储在内存中的某个位置?
仅在编译器运行时。
编译器是否会在看到 0xaaaaaaaa 时将其替换为 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 字符串不再存在。它们为程序员你提供了便利,但计算机在执行时肯定不会关心它们。