我是在大学期间开始接触编程,但是学习的第一门程序设计语言是C语言,但是当时只是简单的学习,能用C语言写一些简单的控制台程序,所以当时对于程序设计来说只是一种玩票性质。
后来也进过实验室,玩过单片机,还接触过汇编,但是最终都没有走下去。
到大四下学期的时候由于毕业设计和工作的需要,开始接触JavaWeb,从此踏上了一条不归路,所以从这个意义上来说Java才是我在编程世界的母语。
从刚开始懵懂地照着葫芦画瓢,到现在接触Java一年半左右的时间,对于一些高级程序设计语言中的基本概念有了一些理解,虽然不知道理解的是否正确,但是还是想记录下来,留待以后进行验证(目前我也在积极的学习JS相关的技术栈,而且计划明年年初的时候学习一下kotlin语言)。
既然我选择了软件开发这个行业同时也热爱着这个行业,那么也就意味着我踏上了一条学习的不归之路。写这篇博客的目的就是谈一谈我对高级程序设计语言的基本理解。当然如果以后有机会我也会系统学习一下编译原理的相关知识,但是这篇文章中不会涉及过多的编译原理相关的内容。
程序设计语言的发展历程
第一台电子计算机出现在20世纪40年代,它使用由0、1序列组成的机器语言编程,这个序列明确地告诉计算机以什么样的顺序执行哪些运算。这样的编程方式枯燥而且容易出错,并且程序的开发人员是完全面向机器的。
走向更加人类友好的程序设计语言的第一步是20世纪50年代早期人们对助记汇编语言的开发。一开始,一个汇编语言中的指令仅仅是机器指令的助记表示。后来,宏指令被加入到汇编语言中。这样,程序员就可以通过宏指令为频繁使用的机器指令序列定义带有参数的缩写。但是使用汇编语言只是在一定程度上提高了程序的可读性,使用汇编开发程序仍然是面向机器的,这要求程序的开发人员对机器要足够熟悉。
我们编写的代码是什么
我们平常使用高级程序设计语言编写的程序代码,其实就是一个文本文件,而每种程序设计语言的文本文件都有自己的扩展名,比如Java程序的源代码文件的扩展名是.java
,JavaScript程序的源代码文件的扩展名是.js
文件的扩展名是什么呢?文件是用来存储数据的,而数据要存储到计算机中肯定要进行编码,那么我们就可以把文件的扩展名理解成一种“协议”,如果一个文件采用某个扩展名,就相当于这个文件遵循了某个协议,就可以被支持这种数据协议的应用程序读取、解析并处理。当然用不支持这种协议的应用程序也能够打开这个文件,但是得到的往往是乱码。
但是程序源代码还有所不同,高级程序设计语言的源代码其本质上还是一个纯文本文件,我们可以用任何一个编辑器打开并查看,而采用特定的扩展名其实是告诉这门高级程序设计语言的编译器,这个文件中的文本遵循了对应的程序设计语言的语法和规范,可以被编译器进行编译。
高级程序设计语言的基本元素
本人没有系统学习过编译原理相关的知识,但是也了解程序编译或者解析的大体流程:源代码中的文本会被程序对应的编译器或者解释器解析成一颗抽象语法树,进而解析成一条一条的指令进行执行(这也太大体了,😅)。
而要想让源代码顺利编译或者解释,我们的源代码就必须遵循程序设计语言所规定的语法,而一门高级程序设计语言往往会规定如下的一些要点。
关键字、标识符和字面量
前面提到,高级程序设计语言的源代码文件本质上是一个纯文本文件,而纯文本文件对于人类来说其实就是一个大字符串,而高级程序设计语言的编译器在对源代码进行编译的时候其实也是在处理一个大的字符串,它会根据特定的规则(主要依靠程序语言中定义的关键字)将源代码的文本映射成一棵抽象语法树,一顿操作之后,代码就能运行了(认真脸)。
而从人类信息文明的发展来看,从刚产生文字到现在为止,人类进行信息记录和传递的方式都是使用“字符串”,而计算机程序的运行是进行数据的处理,我们要向代码中传入数据,然后从代码中接收处理之后的数据;而数据的来源是人类,那么我们可以比较确定的说,计算机程序的最终来源是人类提供的字符串。
就算是java中的Date
这种对象的数据的最终来源也是一个代表了数字的字符串(时间戳)。
高级程序设计语言的源代码本身就是一个字符串,那我们怎么向一个字符串中传递数据呢?这就要依靠高级程序设计语言为我们提供的字面量机制了。高级编程语言中通过使用界定符、特殊格式、关键字等手段为我们提供字面量机制。
在程序设计语言中,字面量常常被分为三类:
- 字符串,但是因为代码本身就是字符串,通常会使用界定符比如
""
来区分是字面量还是程序代码 - 数字,其实就是具有特定格式的字符串,因为其格式特殊,所以不需要界定符
- 逻辑值,true或者false,大部分程序设计语言中都把true和false作为关键字。
思考
- 标识符可以由字母、数字、下划线、$组成,其中数字不能打头。
- 标识符不能是java的关键字和保留字
- 标识符不能包含空格
- 标识符只能包含美元符号($),而不能包含诸如@、#等其他特殊符号(@、#等专门在java编译器自动生成的代码中使用,以进行区分)
以上是摘自《疯狂java讲义》P48中的java标识符的命名规则,其实大部分的程序设计语言的标识符命名规则都是类似的,现在我们来讨论为什么标识符可以包含数字但是不能用数字打头。
上面在介绍字面量的时候提到,数字字面量不需要界定符,也就是编译器在对代码进行编译的时候如果遇到纯数字字符串就认为这是一个数字,注意,数字字面量没有界定符,也就是说数字字面量是直接嵌入到代码中的,编译器对数字字面量的识别仅仅是“这是一个代表数字的字符串”。假如说,标识符的命名规则没有数字不能打头这一说,那么像a=1+b
这句代码中,1是一个标识符还是一个数字字面量?同时就算是限定标识符不能是纯字符字符串也是行不通的,就拿java来说,123L
这是一个数字字面量值,如果放开数字不能打头的限制,对于标识符的解析成本和难度将会大大增加,而对于一门高级程序设计语言来说,对标识符的解析是一个非常频繁的操作,而限制标识符不能以数字打头不会对我们的程序编写造成任何影响,所以标识符名称不能以数字打头无疑是最佳的解决方案。
说到这里我又忍不住提到Java中的int和long的字面量,java中的整形字面量支持四种进制:
- 二进制 以
0b
或者0B
打头 - 八进制 以
0
打头 这一点需要特别注意 - 十进制 默认进制,但是需要注意不要以0打头
- 十六进制 以
0x
或者0X
打头
发现了没有,各种进制还是以数字打头,也就是说在java中只要遇到以数字打头且没有界定符的“标识符”java就认为这是一个数字字面量。
数据类型、操作符和变量
数据类型、操作符和变量可以说是每一门高级程序设计语言的最基本的构件,而且我们日常的编码中很大一部分就是在跟这三者打交道,高级程序设计语言通过这三个基础构件让我们以比较接近自然语言的方式书写程序代码,例如a=b+c
这样的代码相信每个开发人员都写过,不论你使用的是哪一门高级程序设计语言,这样的表达式都是非常常见的;这也使得高级程序设计语言的语义更加明确,代码更加易写。
但是我们在使用高级程序设计语言给我们提供的这些数据类型、操作符和变量的时候,高级程序设计语言到底问我们做了什么事情呢。
在我的理解中,数据类型、操作符以及变量其实就是高级程序设计语言为我们封装的一系列内存操作,比如在32位的计算机系统中,如果直接使用汇编我们需要自己对内存进行寻址,同时将数据加载到一个寄存器中,然后再用相应的汇编指令让CPU对我们加载到寄存器中的数据进行运算;而在高级程序设计语言中,这一系列的操作被一个简单的表达式a=b+c
完成,其中的a、b、c在这里是变量(当然一个表达式中也可以存在字面量值)帮助我们完成了寻址的操作,同时变量所具有的数据类型规定操作的内存的大小和内存中数据的解释方式,操作符规定了对这些数据进行的操作。
对以上的理解稍微总结一下,在高级程序设计语言中:
- 变量其实是引用了一个内存地址,它帮助我们完成对内存的寻址操作。
- 数据类型定义了对应的变量所要操作的内存的长度,以及对内存中数据的解释方式。
- 操作符定义了要对对应的变量指向的内存地址中对应长度的数据进行的操作。
这样,开发人员就从底层硬件中解脱了出来,让我们更加关注于程序的逻辑。
当然,现在很多的程序设计语言是运行在某个平台(宿主环境,可以是操作系统,也可以是其他的应用程序,比如JavaScript是运行在浏览器中的;SQL是运行在数据库管理系统中的;Java是运行在JVM中的,而JVM又是运行在操作系统中的;etc...),而高级程序设计语言的发展又依赖于编译技术等底层技术的发展,这里不进行过多的介绍。
以上的理解均是个人经验和理解,如有不同的看法欢迎留言进行讨论