一、初步理解
无论变量还是常量,都是如今编程语言中很通用的概念,只不过在不同编程语言中的实现细节、使用方法会有些许差异。
首先来看看常量。
所谓常量,大致意思就是不可修改、保持不变的量,它对应于编程中的一个基本底层需求:存放不希望被修改的数据。
比如数学中的Π
、e
等常数,有修改的必要吗?
一旦允许它被修改,那么在大型项目中,我们就无法保证别人不会修改它,这就很容易引发事故。
这里举的数学常数可能大家都知道,觉得不会有这种问题,可如果是天文常数、物理常数呢?那么多!你也不可能让每个人都知道它是个常数,不可更改吧?
为了杜绝这种情况,大部分语言本身都是支持常量的,并且基本使用的都是const
关键字来声明一个常量。
并且由于常量本身不可修改的限制,一般都会要求你在声明变量的同时进行赋值,以C/C++为例:
const int a=10;//常量
const int b;// 错误,必须在声明的同时对其进行赋值,因为后续不允许对它进行修改了。
之后,一旦你试图去修改这个常量,那么你写的代码根本就通不过编译,这就从根本上杜绝了修改的可能性。
与之对应的就是变量,也就是可变的量,它对应于编程的一个基本底层需求:动态存储、修改数据。
编程语言的根本目的,是让我们人类可以用一种尽可能简单、优雅、准确的方式去告诉计算机怎么处理数据。
如果你觉得编程语言一点都不简单,也并不是你的错,因为随着编程语言本身越来越简单、优雅、强大,它能实现的功能就会越多,社会对其的期望就会越大,对应的学习要求、难度等等也会随之上升,但实际上这并不是编程语言本身的问题,相比于以前的编程语言(机器码、汇编),现代编程语言都是越来越简单、优雅的(go、rust、kotlin)。
无论是软件、游戏,还是视频、音乐,其本质都是数据,更准确的来说,都是一堆0
和1
。
虽然常量也可以存这些数据,但一般没人这么做:用十六进制编辑器打开这些文件,然后再将里面的数据直接复制到代码中,并赋值给常量,就能将数据编译进文件中。
那么这时候就需要变量了,它可以动态修改数据,这里还是以C/C++为例:
int a;
a=10;
能动态修改就意味着,你可以在程序运行过程中来接受、处理数据。
比如当你用音乐播放器打开一个音乐文件、视频播放器打开一个视频文件,这里的播放器就是已经编译生成的可执行文件了。
而这些文件数据就是在程序运行起来之后,才动态读取进行处理的,这就是变量的作用。
一般就实际开发而言,变量肯定比常量用的多。
二、命名规则
无论变量还是常量,说白了就是给一块内存取一个名字,既然是名字,那肯定就有一定的规范,不能让你乱取。
比如在编程语言中通常用=
符号来赋值,如果你将这个符号作为变量名一部分,就肯定会问题,编译器无法识别。
目前就我所知,也许是编程传统所致,大部分正规的编程语言,所使用的变量命名规则字符集都是一样的:
- 字母:a-z A-Z
- 数字:0-9
- 下划线:_
之所以有这个正规的限定,是因为有些离奇的编程语言,编程过程中甚至都不需要用字母,比如:Brainfuck。
上面的字母、数字、下划线可以任意组合,就可以作为变量名:
int aAv90=10;
int _a8z_=20;
但唯独不可以将其数字放在最前面:
int 9abc_=10;
这种将数字放在最前面的就是错误的命名方式。
其原因在于,将数字放在名字前面,编译器无法识别这是一个数字还是名字,因为编程语言中是存在书写其它格式数字需求的,比如
0x12
,以数字0
开头,并且既有数字也有字母,但这是一个十六进制的数字,而不是变量名。
其实不仅仅是变量名,程序中任何需要你自己取名的地方,基本都是这个规则。
而在一些现代语言中,比如rust
、go
,甚至会给一些特殊的名字赋予一些特殊的含义。
比如C++
中暴露方法是在类中使用public
关键字,比较麻烦。
而在Go
中直接就默认规定了在模块中,函数名的首字母大写就是公开的,小写就是私有的。
在rust
语言中,如果一个变量声明了但不使用,编译器就会报警告,那么你就可以用下划线_
作为变量名的开始,这样程序就不会报警告了。
三、作用域
取名字,听着简单,但其实有些时候并不简单,取名这个过程的本质,是给申请的一块内存取名字,方便我们后面使用。
然而一个类型的变量,比如时间,常见的名字也就那么几个,以至于很多时候就出现一种用无可用的地步,于是就开始添加数字、随便使用个字母等等。
比如:
int time1;
int t1;
而作用域这个概念,在一定程度上就可以解决这个问题。
比如C/C++中的作用域就是一个大括号表示的:
int time = 1;//外层定义一个time变量
{
time = 3;//使用的外层time,从1改为3
int time = 2; //内层再定义一个time变量,正确,不在同一个作用域
time = 4;//使用的内层time,从2改为4
}
事实上大多数语言都继承了这个传统:用大括号限定一个变量的作用范围。
但也有例外,比如
python
,就是用空格代替大括号来限定,
通过这种限定一个变量使用范围的方式,就可以让我们重复使用一个名字来命名变量了。
并且它有一套固有使用的规则:从使用变量的地方往前看,使用最近的那一个。
如果该作用域没有这个变量名,那就会往外层找,如果一直找到最外层都没有,那就报错。
注意,它不会向内层寻找,比如下面这种代码就是错误的:
{
int time = 2; //一旦脱离这个作用域,这个变量就不可用。
}
time = 5; //错误,只会向前找同作用域以及外层的变量
上面代码中的注释很重要:脱离作用域,变量就不可用。
当然,你如果非要在
C/C++
中将它的地址取出来拿到外面用,兴许也能用,不过这种做法是绝对不推荐的,因为脱离作用域它就会被丢弃,那么这块内存上的数据就处于未知状态。
而处于最外层的,我们一般又将其称为全局变量,还是以C/C++
为例:
int t=1; //全局变量
int main() {
}
也就是它不在任何一个大括号之内,就叫做全局。
此时它就在最外层,那么所有它后面的代码,如果使用t
变量,就都会从当前层开始向前、向上找,如果都没有,那就会使用这个全局变量。
虽然这里都是用C/C++代码为例,但在大多数语言中都是通用的,只是实现方式不一样而已。