变量到底是什么
第 8 集《变量到底是什么 99%的人理解错了》
开场
听懂不算会,敲出来才算。
大家好,我是村长。
上期视频,我们聊了存储程序的思想。
记住那句话:程序指令和数据都会进内存,这是程序自动运行的基础。
本期:咱们聊变量。变量又是一个天天用,却未必真正理解的概念。
今天我就带你从底层视角,重新认识变量。
(LOGO 动画)
知识点
- 一、老张的故事
- 二、变量是什么
- 三、变量的本质
- 四、变量名啥时消失的
一、老张的故事
这里有一个老张的故事:
Console.WriteLine("老张今年刚满18岁,大家都喊他老张");
Console.WriteLine("老张觉得这个称呼太老了,想让大家叫他小张");
Console.WriteLine("可村里人叫习惯了,还是喊他老张");
Console.WriteLine("18岁的老张就这么被叫了一辈子老张");
假如有一天,我想把“老张“改为“老李“,
“老张“这个名字在故事中一共出现了 6 次,因此,我必须手动修改 6 次。
还好,只改 6 次,还能忍。
设想:如果有几百行代码,姓名反复出现上百次
光是改名,就能改到怀疑人生。
所以,程序需要一个东西,来管理这些反复出现的数据。
解决这个问题的方法,就是使用变量。
:
string characterName = "老张";
string 表示变量的类型是字符串,characterName 是变量名,意思是“角色名字”。
赋值之后,我们就能用 characterName代替“老张”了。
string characterName = "老张";
Console.WriteLine(characterName + "今年刚满18岁,大家都喊他" + characterName);
Console.WriteLine(characterName + "觉得这个称呼太老了,想让大家叫他小张");
Console.WriteLine("可村里人叫习惯了,还是喊他" + characterName);
Console.WriteLine("18岁的" + characterName + "就这么被叫了一辈子" + characterName);
(选择第一个“老张",接着按下快捷键Shift + Alt + ; 可以选择所有“老张“,然后把老张修改为 characterName)
点击运行,此时,读取变量characterName,拿到的还是老张。
把第一行的“老张“修改为“老李“,再运行一次,可以看到:所有地方全部改为老李了。
。
二、变量是什么
关于变量到底是个啥?江湖上流传着很多说法:
有人说:变量是存储同类型数据的容器。
意思是:变量很像一个容器,可以存储同类型的数据。
也有人说:变量是值可以变化的量。
意思是:容器中的数据可以修改,你可以随时给它赋一个新值,把旧值覆盖掉:
age = 19;
还有人说: 变量是有名字的内存空间。
意思是:变量的本质是内存空间,你可以使用名字访问内存空间。
这句话甚至被很多教材当成标准答案。
奇奇:我觉得这三种说法都挺有道理的,难道不对吗?
村长:这些说法,并不是完全错误。
它们只是程序员站在使用的角度,告诉你变量怎么用,帮助你快速入门。
但它们都没有说到变量的本质。
所以,你会写变量,却未必真正理解变量。
理解变量,我们不能只看现象,要看 C# 官方,到底是怎么定义它的。
三、变量的本质
奇奇:刚才的那些说法都不够本质,那变量的本质到底是什么?你直接告诉我答案吧!
村长:答案就写在 C# 语言规范(ECMA-334)的原文里,翻译成中文,只有8个字:
变量表示存储位置。
虽然只有8个字,但信息密度极高。
真正决定这 8 个字含义的,不是开头的"变量"二字,也不是后面的"存储位置",而是中间的"表示"二字。
这两个字用的非常微妙。
“表示”不等于“是”。变量表示存储位置,不等于“变量是存储位置“。
既然变量表示存储位置,那么程序员就可以通过变量名,去访问存储位置。
如果没有变量,我们当然也可以直接使用内存地址编程。
只是那些地址既难记,又容易写错。
所以,变量存在的意义,就是让程序员不用去记那些晦涩的地址,而是通过一个有意义的名字去操作它们。
这里的“存储位置“不是抽象概念,而是一块实实在在的存储空间。
上期视频我们讲过,指令和数据都会进入内存。
但为了极致性能,编译器有时也会把数据直接丢进 CPU 的寄存器里。
所以,这里的“存储位置“大多数情况下可以理解为内存,但也可能是寄存器。
四、变量名啥时消失的
奇奇:那是不是是说:变量名只是写代码的时候才有用,程序一旦运行起来,它就没用了吗?
村长:没错!你已经抓住重点了。变量名就是给程序员看的。 程序运行时,CPU根本不会根据变量名找数据,它只认二进制的内存地址。
奇奇: 那变量名是什么时候“消失”的呢?
村长:第一次编译后就消失了。
就在你点击“开始执行“按钮的瞬间,先后会触发两次编译:
输出窗口记录了第一次编译的整个过程,
第一次编译的主人公是:C#编译器
它把我们刚刚写的代码翻译成了中间代码,中间代码就是“半机器码“,
变量名被编译成存储编号之后,就消失了,最终,把中间代码保存在两个文件中,一个是Lesson8.dll 文件,另一个是Lesson8.exe 文件。
控制台窗口显示了第二次编译的结果:
在执行 Lesson8.exe 文件时,JIT 编译器出场了。
JIT 编译器把中间代码翻译成了二进制指令。
这时,存储编号就变成了CPU可以直接访问的二进制内存地址。
一顿操作猛如虎,最终把“老李”打印在了屏幕上。
最后,记住一句话:变量名只属于程序员;内存地址才属于计算机。第一次编译,就是两者交接工作的那一刻。
结束语
本期视频就到这里。我是村长。
如果这个视频对你有帮助,记得点赞、收藏、关注。 感谢观看,我们下期见。