返回

编程的基础知识:变量的背后故事

前端

当我们声明变量的时候,本质上就是使用了所有编程语言的基本功能之一:变量存储功能。这种功能允许我们存储变量中的值,并在以后对这个值进行访问或者修改。那么,当我们声明一个变量时,计算机内部到底发生了什么呢?我们从编译原理开始说起。

分词/词法分析:从字符串到代码块

这个过程一般会把由字符串分解成有意义的代码块,这些代码块被称为词法单元。例如,当我们编写代码var a = 2;时,词法分析器会将其分解为以下几个词法单元:

var: 这是一个,表示我们要声明一个变量
a: 这是一个标识符,表示变量的名称
=: 这是一个赋值运算符,表示我们要将值2赋给变量a
2: 这是一个字面值,表示我们要赋给变量a的值
; : 这是一个分号,表示这一行代码的结束

语法分析:从代码块到语法树

接下来,语法分析器会将这些词法单元组合成语法树,语法树是一种数据结构,它可以表示代码的语法结构。对于我们的代码var a = 2;来说,语法树如下:

程序
  |
  +- 变量声明
       |
       +- 变量类型 var
       |
       +- 变量名 a
       |
       +- 赋值运算符 =
       |
       +- 值 2
       |
       +- 分号 ;

语义分析:检查代码的合法性

语义分析器会检查语法树,以确保代码是合法的,并且没有语法错误。对于我们的代码var a = 2;来说,语义分析器会检查以下几点:

  • var关键字后面是否跟着一个变量名
  • 变量名是否已经声明过
  • 赋值运算符左右两边的类型是否兼容

代码生成:将代码转换成机器指令

如果代码通过了语义分析,那么代码生成器就会将代码转换成机器指令。机器指令是一种低级语言,它可以被计算机直接执行。对于我们的代码var a = 2;来说,代码生成器可能会生成以下机器指令:

mov eax, 2
mov [a], eax

第一条指令将值2加载到eax寄存器中,第二条指令将eax寄存器中的值存储到变量a的内存地址中。

变量的存储:在内存中分配空间

当我们声明一个变量时,编译器会为这个变量在内存中分配空间。这个空间的大小取决于变量的数据类型。例如,对于一个int类型的变量,编译器会分配4个字节的空间。

变量的访问:通过内存地址获取值

当我们访问一个变量时,编译器会生成一条指令,这条指令会将变量的内存地址加载到一个寄存器中,然后从这个寄存器中读取变量的值。例如,对于我们的代码a = a + 1;,编译器可能会生成以下机器指令:

mov eax, [a]
add eax, 1
mov [a], eax

第一条指令将变量a的内存地址加载到eax寄存器中,第二条指令将eax寄存器中的值加1,第三条指令将eax寄存器中的值存储回变量a的内存地址中。

变量的修改:通过内存地址写入值

当我们修改一个变量的值时,编译器会生成一条指令,这条指令会将变量的新值写入到变量的内存地址中。例如,对于我们的代码a = a + 1;,编译器可能会生成以下机器指令:

mov eax, [a]
add eax, 1
mov [a], eax

第一条指令将变量a的内存地址加载到eax寄存器中,第二条指令将eax寄存器中的值加1,第三条指令将eax寄存器中的值存储回变量a的内存地址中。