知道 &
取址操作是
每一个变量标识符在编译期间,编译器会为它们创建一个符号表,其中存放着变量标识符相应的各种属性,如类型、地址标识等
那么问题来了:
- 每创建一个变量就要创建一个符号表的话,是不是会占用很大的内存?
- 整个创建变量并取址的完整过程在系统里是怎么进行的?
代码中的变量在“编译”的时候会全部转换为指针进行存储。
这就是静态语言和动态语言的区别,静态语言编译之后,在内存里将只存在指针。
指针就是变量,变量就是指针。
简单概括一下吧。对全局变量取址的话,汇编代码会直接有一个立即数在里面,作为地址。这个立即数是链接时候确定的。
局部变量的话,编译器首先会在栈上给他留个空间,否则没有地址,然后以ebp减偏移的方式去计算,偏移在编译这段函数的时候就计算好了。编译之后的代码里没有变量名字,所以不可能有根据名字去地址这样一个操作,顶多用lea做一下运算。
建议看看《csapp》,前半部分有讲。
反编译一下,或者用 gdb 就知道了。
每创建一个变量就要创建一个符号表的话
可能是原文的误解,“每一个变量标识符在编译期间,编译器会为它们创建一个符号表,其中存放着变量标识符相应的各种属性,如类型、地址标识等”,这里面的他们,值得是所有的变量,而每个变量这是符号表里面的一个符号而已,所以,不是“为每个变量创建一个符号表”。
占用的内存会大,也可能很大,但是一般优秀的编译器都会优化。
而且,注意这段话里面的一个词语——在编译期间。
第二个问题,读读汇编语言里面的寻址,大概就明白了。
在C语言中,每个变量其实都是地址的一个别名。这个地址可能是 0x1002
,为了容易记忆,我们就给他起了一个名字,叫 a
。
其实,a
就是 0x1002
,0x1002
就是 a
。
int a = 4;
这个语言大概会编译成
MOV [1002h] 4
; 4是立即数
; []直接寻址
意思是把 a
放入 0x1002
的地址里面。
当我们使用变量时,其实使用的是地址里面的具体值。这个值是二进制的01串,这个值具体是多少,取决于数据类型。
对于指针操作呢,则直接操作的地址
int *p = &a;
这句话定义了一个变量 p
, p
是一个整数,p
的地址可能是 0x1024
, 在这个地址里面,存放着 a
(注意,不是 a
地址里面的值,而是 0x1002
)。
MOV P 1024h
MOV [P] 1002h
; 1024是立即数,
; []直接寻址
为了便于理解,简化了好多。