编程语言 - 介绍与分类

1.应用场景

目的就是想更加清楚地知道编程语言的分类,弄清楚一直以来的困惑.

建议阅读<<计算机组成原理>><<操作系统>>等相关书籍

2.学习/实践

1. 文档

编程语言 - 强弱/动静态类型

C语言 - 介绍_william_n的博客-CSDN博客

C语言这么厉害,它自身又是用什么语言写的?_码农翻身-CSDN博客

编程语言的发展史 - KAMNA - 博客园

机器语言与汇编语言_Sucker For Pain-CSDN博客_机器语言和汇编语言

汇编语言和机器语言是一一对应的吗?

计算机和编程语言的发展历史

计算机组成原理-王道考研系列

汇编语言从0开始 到C语言 - 网易云课堂

04 | 编程语言的进化-极客时间

2. 整理输出

编程语言层出不穷, 如果想弄清楚这到底是怎样的一回儿事儿, 就必须要将其发展的历史理清楚.

这里要插入一段话,--- 因为为了避免你总想学很多的编程语言

Alan Perlis(ACM第一任主席,图灵奖得主,1922-1990)曾经说过:“如果一门语言不能影响你对编程的想法,那它就不值得去学”。另一种观点是,有时候你不得不学一点C++(更可能是javascript和Flash Flex之类)的皮毛,因为你需要接触现有的工具,用来完成特定的任务。但此时你不是在学习如何编程,你是在学习如何完成任务。

2.1 简述

计算机语言的发展经历了面向机器的机器语言 和 汇编语言, 面向问题的高级语言.

其中高级语言的发展真正促进了软件的发展, 它经历了从科学计算和工程计算的FORTRAN, 结构化程序设计PARSCAL 到 面向对象的C++和适应网络环境的Java.

2.2 软件分类

软件按照其功能分类可以分为 系统软件 应用软件

系统软件

一组保证计算机高效, 正确运行的基础软件, 通常作为系统资源提供给用户使用.

主要有操作系统(OS), 数据库管理系统(DBMS), 语言处理程序, 分布式软件系统, 网络软件系统,标准库程序, 服务型程序等.

应用软件

指用户为解决某个应用领域中的各类问题而编制的程序。

如各类科学计算类程序, 工程设计类程序, 数据统计与处理程序等. -- 绝大数程序员都是做这类的的软件

2.3 编程语言发展的三个阶段

第一代语言: 机器语言

又称为二进制语言, 需要编程人员[好像那时候还不叫程序员]记忆每一条指令的二进制编码, 机器语言是计算机唯一可以直接识别和执行的语言. --- 现在也是,未来还是

第二代语言: 汇编语言

汇编语言用英文单词或缩写代替二进制的指令代码, 更容易人们记忆和理解, 汇编语言的程序必须经历过一个称为汇编程序的系统软件的翻译, 将其转换为计算机的机器语言后, 才能在计算机的硬件系统上执行.

第三代语言: 高级语言

高级语言(如, C, C++, Java等) 更多地是为了方便程序设计人员写存储解决问题的处理方案和解题过程的程序. 通常高级语言需要经过编译成汇编语言程序, 然后经过汇编操作得到机器语言程序, 或者直接由高级语言程序翻译成机器语言程序.

Note

C语言之前还有A,B语言,更多细节请自行搜索

另外查看

04 | 编程语言的进化-极客时间

2.4 分类

机器语言   汇编语言   高级语言 , 介绍见上面

额外补充:

04 | 编程语言的进化-极客时间

从语言的执行器的行为看,出现了这样三种分类的语言。编译的目标文件为可执行程序。典型代表是 Fortran、C/C++、Go 等。

生成跨平台的虚拟机字节码,有独立的执行器(虚拟机)执行字节码 。典型代表为 Java、Erlang 等。

直接解释执行。典型代表是 JavaScript。当然现在纯解释执行的语言已经不多。大多数语言也只是看起来直接执行,内部还是会有基于字节码的虚拟机以提升性能。

2.5 编译器

机器语言是计算机唯一可以直接识别和执行的语言, 所以如汇编语言和高级语言都是需要一个直接或者间接转换为机器语言的过程, 这个过程由一个工具负责, 就是编译器[针对编译型语言而言], 解释性语言则另外一种执行方式. 见下方:

翻译程序, 汇编程序, 编译程序, 解释程序的区别和联系?

翻译程序是指把高级语言源程序翻译成机器语言程序(目标代码)的软件.

翻译程序有两种, 一种是编译程序, 它将高级语言源程序一次全部翻译为目标程序, 每次执行程序时, 只要执行目标程序, 因此, 只要源程序不变, 就无须重新编译, 请注意同一种高级语言在不同体系结构下, 编成的目标程序是不一样的, 目标程序与体系结构相关, 但仍不是计算机硬件能够执行的程序. 

另外一种是解释程序, 它将源程序的一条语句翻译成对应的机器目标代码, 并立即执行, 然后翻译下一条源程序语句并执行, 直至所有源程序语句全部被翻译完并执行完. 所以解释程序的执行过程是翻译一句执行一句, 并且不会生成目标程序.

由于解释程序要边翻译边执行, 故而执行速度相比于编译程序较慢. 

为增加对该过程的理解, 附C语言编译连接过程:

源程序(.c)-----c编译器--->汇编源程序----汇编程序---->目标程序----链接程序--->可执行程序[即传说中的二进制可执行文件]

汇编程序也是一种语言翻译程序, 它把汇编语言源程序翻译为机器语言程序. 汇编语言是一种面向机器的低级语言, 是机器语言的符号表示, 与机器语言一一对应.

编译程序与汇编程序的区别: 如果源语言是诸如C, C++, Java等"高级语言", 而目标语言是诸如汇编语言或机器语言之类的"低级语言", 这样的一个翻译过程称为编译程序.  如果源语言是汇编语言, 而目标语言是机器语言, 这样的一个翻译程序称为汇编程序.

2.6 编译器演进

我们知道在最开始的时候, 是没有所谓编译器的, 先从机器语言说起,看看怎么办。

首先, 我们知道, 机器语言可以直接被CPU执行,不需要编译器。可是机器语言不便于记忆, 所以出现了汇编语言[机器语言的助记符]

汇编语言也需要编译成机器语言才能执行,没办法只能用机器语言来写这第一个编译器了以后就不用了

汇编语言的问题解决了,就往前迈进了一大步,这时候就可以用汇编语言去写C语言的编译器我们说这是C编译器的老祖宗。

有了这个老祖宗,就可以编译任意的C语言程序了,那是不是可以用C语言本身写一个编译器?只要用老祖宗编译一下就可以了。

OK, 这么一层层上来,终于得到了一个用C语言写的编译器, 真是够麻烦的。 

到这个时候,之前那个汇编写的C语言编译器就可以抛弃了。 // 暂未想明白?怕是要到<<编译原理>>中解惑, 关键点在于: 编译器也要经过编译成二进制,才能发挥编译作用。【已经想明白了, C编译器的老祖宗的可执行代码是二进制, C语言写的编译器的可执行代码也是二进制,其中关键点就是程序分为两部分:src 源代码,与dest 目标程序, 前者是高级语言编写的源代码,后者经过上一个编译器编译后的新的编译程序, 然后重复这个过程,理论是可无止境地创造编程语言,在任何一层,当然除了机器语言~

当然,如果在C语言之前,已经出现了别的高级语言,例如 Pascal,那就可以用Pascal来写一个C语言的编译器。

第一个Pascal的编译器据说是使用Fortran写的。而做为第一个高级语言的Fortran [刚刚搜索了下,似乎还有一些人在使用],它的编译器应该是汇编语言写的。

可能有人问:我用汇编写一段Hello World都很麻烦,居然有人可以用它写复杂的编译器?这可能吗?

当然可能,在开发第一代Unix的时候,连C语言都没有, Ken Thompson 和 Dennis Ritchie 可是用汇编一行行把Unix敲出来的。   

WPS第一版是求伯君用汇编写出来的, Turbo Pascal 的编译器也是Anders 用汇编写出来的,大神们的能力不是普通人能想象得到的。 

对于编译器来说,还可以采用“滚雪球”的方式来开发:

还是以C语言为例,第一个版本可以先选择C语言的一个子集,例如只支持基本的数据类型,流程控制语句,函数调用......[应该说是新的高级语言最基础的模块,能够支持未来该语言的所有高级特性,如如果不能支持,可能就要在最初版的子集使用汇编再追加] 我们把这个子集称为C0。

然后用汇编语言写个编译器,只搞定这个语言的子集C0,这样写起来就容易不少。

C0这个语言可以工作了,然后我们扩展这个子集,例如添加struct,指针......  ,把新的语言称为C1。 

那C1这个语言的编译器由谁来写?自然是C0。

等到C1可以工作了,再次扩展语言特性,用C1写编译器,得到C2。 

然后是C3, C4......  最后得到完整的C语言。

这个过程被称为bootstraping , 中文叫做自举。

插入

常见语言的易用性, 性能, 以及流行度见下表,

圆圈大小代表流行度

问题:

Go语言能否替代php做互联网网站开发?

在整体易用性、流行度方面Go和PHP还完全不是一个层次。

PHP如日中天,但有缓慢衰减之势。而Go还只是新兴的小众语言

补充

下面对比各种语言之间的区别,可以认真阅读

2.7 编程语言发展趋势

不好说,但是未来应该会出现或者说已经出现了更加高级的语言, 更高层次的封装抽象, 以模块为单位, 更加简单. 但是底层的东西还是始终不能丢弃. 因为这是根基, 根基的重要性不言而喻. // 个人认为

2023118 周六

2.8 各语言优缺点对比(学习成本)

后续补充

...

3.问题/补充

1.何为脚本语言?

常听到PHP, JavaScript, shell 等脚本语言?

所谓“脚本语言”(script language),指的是它不具备开发操作系统的能力,而是只用来编写控制其他大型应用程序(比如浏览器)的“脚本”。

例子: -- 阮一峰

另见维基百科:

https://zh.wikipedia.org/wiki/脚本语言

截图:

08 | 操作系统内核与编程接口-极客时间

2. 先有编程语言,还是先有操作系统;

对于第一个问题:先有编程语言,还是先有操作系统?

这个问题的答案比较简单,先有编程语言。

之所以有这个疑问,是因为两点:

其一,大部分人习惯认为运行软件是操作系统的责任。少了责任方,软件是怎么跑起来的?但实际上软件跑起来是很容易的,看 BIOS 程序把控制权交给哪个软件。

其二,大部分常见的应用程序都直接或间接依赖操作系统的系统调用。这样来看,编程语言编译出来的程序是无法脱离操作系统而存在的。

但是实际上常见的系统级语言(比如 C 语言)都是可以编写出不依赖任何内核的程序的。

补充:

网友-new life

老师 但是实际上常见的系统级语言(比如 C 语言)都是可以编写出不依赖任何内核的程序的。这句话是说我们编写的程序 没有操作系统也可以跑起来是吗

作者回复:

要让引导程序给你执行权才行,如果你写一个不依赖操作系统的程序,但是要让操作系统执行它是行不通的,因为操作系统接管了所有的资源,你不依赖它什么也干不了(没有权限)。所以这种写法基本上只适合写另一个操作系统

xiaobang

请教下最后总结图里“编程语言文法和核心库” 为什么在操作系统下面?

还有编程语言的编译工具在操作系统看来也只是一些特殊的应用程序吧 另外现在有用高级语言比如go Java之类写操作系统的传闻,我一直有个疑问这些高级语言依赖的运行时是怎么搞定的,毕竟写操作系统时不能再依赖操作系统的支持了

作者回复: 编程语言本质上是cpu能力的提炼,是比操作系统还要底层吗东西。但是编程语言它又要体现每一个层次平台的能力,包括操作系统、网络、云计算等。

3. 编程语言怎么做到自举的(比如用 C 语言来实现 C 语言编译器);

从鸡生蛋的角度,编译器的进化史应该是这样的:

先用机器码直接写第一个汇编语言的编译器,然后汇编语言编译器编出第一个 C 语言编译器。

有了 C 语言编译器后,可以反过来用 C 语言重写汇编语言编译器和 C 语言编译器,做更多的功能增强。

这个过程理论上每出现一种新 CPU 指令集、新操作系统,就需要重新来一遍。但是人是聪明的。所以交叉编译这样的东西产生了。

所谓交叉编译就是在一种 “CPU + 操作系统” 架构下,生成另一种 “CPU + 操作系统” 架构下的软件

这就避免了需要把整个编译器进化史重新演绎一遍。

可以详细看上面 2.6

4. 对于第三个问题:操作系统能够做到自身迭代本操作系统(自举)么?

当然可以。

通常一门新的操作系统开发之初,会用上面提到的交叉编译技术先干出来,然后等到新操作系统稳定到一定程度后再实现自举,也就是用本操作系统自己来做操作系统的后续迭代开发。

5. 交叉编译不是很理解

作者回复:

其实理解清楚一个实质:编译器就是把高级语言翻译成为机器码,更抽象说,它其实就是格式转换器。目标格式是不是编译器正在运行的环境并不重要,只不过如果目标格式刚好是当前机器的CPU+操作系统,那么目标格式就可以直接执行,否则就编译出一个当前环境下无法执行的目标格式,这种情况就叫交叉编译。

百度百科

。。。

交叉编译这个概念的出现和流行是和嵌入式系统的广泛发展同步的。我们常用的计算机软件,都需要通过编译的方式,把使用高级计算机语言编写的代码(比如C代码)编译(compile)成计算机可以识别和执行的二进制代码。比如,我们在Windows平台上,可使用Visual C++开发环境,编写程序并编译成可执行程序。这种方式下,我们使用PC平台上的Windows工具开发针对Windows本身的可执行程序,这种编译过程称为native compilation,中文可理解为本机编译。然而,在进行嵌入式系统的开发时,运行程序的目标平台通常具有有限的存储空间和运算能力,比如常见的ARM平台,其一般的静态存储空间大概是16到32MB,而CPU的主频大概在100MHz到500MHz之间。这种情况下,在ARM平台上进行本机编译就不太可能了,这是因为一般的编译工具链(compilation tool chain)需要很大的存储空间,并需要很强的CPU运算能力。为了解决这个问题,交叉编译工具就应运而生了。通过交叉编译工具,我们就可以在CPU能力很强、存储空间足够的主机平台上(比如PC上)编译出针对其他平台的可执行程序。

要进行交叉编译,我们需要在主机平台上安装对应的交叉编译工具链(cross compilation tool chain),然后用这个交叉编译工具链编译我们的源代码,最终生成可在目标平台上运行的代码。常见的交叉编译例子如下:

1、在Windows PC上,利用ADS(ARM开发环境),使用armcc编译器,则可编译出针对ARM CPU的可执行代码

2、在Linux PC上,利用arm-linux-gcc编译器,可编译出针对Linux ARM平台的可执行代码。

3、在Windows PC上,利用cygwin环境,运行arm-elf-gcc编译器,可编译出针对ARM CPU的可执行代码。

维基百科
交叉编译器是一种能够为编译器所在平台以外的平台创建可执行代码的编译器。例如,在PC上运行但生成在Android智能手机上运行的代码的编译器就是交叉编译器。

从一个开发主机编译多个平台的代码需要交叉编译器。在目标平台上直接编译可能是不可行的,例如在计算资源有限 的嵌入式系统上。

交叉编译器不同于源到源编译器。交叉编译器用于机器代码的跨平台软件生成,而源到源编译器将文本代码从一种编程语言转换为另一种编程语言。两者都是编程工具。

FYI
https://en.wikipedia.org/wiki/Cross_compiler
https://zh.m.wikipedia.org/zh-hans/%E4%BA%A4%E5%8F%89%E7%B7%A8%E8%AD%AF%E5%99%A8
交叉编译_百度百科

6. cpu不需要检查是否发生了中断,它的原理类似于开关和灯泡的关系,当开关合上,灯泡就会亮,灯泡不需要定期检查开关是否合上了

作者回复: 👍

补充:

网友-M

请问一下cpu是如何检查是否有中断的。是怎么及时知道发生了中断?每执行完一条指令都去检查一次吗?

作者回复:

挺好的问题。硬件中断和软中断不一样。硬件中断你可以理解为总是会定期检查。软中断本身是一条指令,所以不存在检查这样的概念。

马留

对“硬件中断要周期性检查”不太理解。CPU和外设交互,有三种方式:轮询 中断 DMA。对CPU来说,硬件中断来自于外部触发,是异步的,怎么需要周期性检查呢?

作者回复: 硬件中断周期性检查是一个错误的表述

网友-二星球

老师好,我这有个问题,就是有中断必有对应的中断处理程序,那么执行中断处理程序会不会像普通线程那样抢占cpu资源呢,如果没抢到还要等一等?还有就是一下同时来了十几个中断,那么怎么处理呢?

作者回复: 软中断你可以把它理解为虚函数调用,本来就占着cpu资源呢,不需要等。一下子来很多中断是可能的,在硬件中断的情况下,这时候会根据中断优先级响应。

陈光

老师,CPU划定权限范围,操作系统负责分配权限,可以这样理解吗?另外,为什么CPU会需要定期检查“硬中断”而不需要定期检查“软中断”?是因为软中断是更“高”一层的中断吗?

作者回复: 软中断就是一条cpu指令,遇到了执行就行了。硬件中断我觉得其实也不存在定期检查,所谓检查只是从软件思维去理解硬件而已,前面有人举了开关合上灯就亮了是一个挺恰当的比喻,灯并没有去不停检查开关是否合上,所谓检查只是从软件去理解这种现象的一种想象。

7. 晓凉

优秀的架构设计能带来的好处可能超出架构师最初的预想,例如Linux系统的容器特性,现在成了云计算领域的重要基础技术,成就了一个领域。基于前辈大师的优秀架构,现在的信息世界才能如此生生不息。优秀的架构不仅能解决当前的实际问题,更具有理论上的优雅,像一种真理,可照亮未来。

作者回复: 我们日常所见明明有无数设计精良的例子,所以我们其实不需要一上来去设计新的例子,从这些最宏大的例子学起

8. 行者

关于动态库,是不是可以理解为:提供动态库不是操作系统的责任(因为其他语言完全可以通过系统调用来自己实现动态库的功能),只是操作系统为了方便其他语言(减少不必要的冗余)而做的多余的事?

作者回复: 是的

9. Geek_gooy

前面提到:

操作系统与我们编写的软件并不同属一个进程,两边的内存地址空间都是独立的。

后面提到: 当然你可能要问:既然操作系统内核和我同属一个地址空间,……

老师,这是不是矛盾啊?中间的解释看不懂,求形象举例。

作者回复:

第一句话是逻辑上的,操作系统的进程,和用户进程都有独立的地址空间。

但是细究细节,可以发现操作系统进程和所有用户进程有一部分地址空间又是共享的,里面就是操作系统的内核。

10. 行者

有个疑问,CPU怎么区分操作系统和常规软件,从而做到给予不同软件的CPU权限?是BIOS在决定将软件运行权时就直接交给该软件(OS)所有权限的吗?

作者回复: cpu 不区分操作系统和软件,它眼里没有软件的概念。cpu只有一个状态码代表当前的执行权限等级(比如用一个寄存器来表示权限等级)。这也是为什么有一些恶意软件可以利用漏洞提权。

11. 幻灰龙

跨语言交互能增加理解,特别是有虚拟机的语言之间,例如在C#或者Java里跟Lua交互。

作者回复:

挺好的补充。语言与语言的交互分为两种,一种是进程内,一种是进程间。

进程内的,一方作为宿主语言,一方作为嵌入式脚本。

这种类型的机制本质上通常最终归结为宿主语言与C语言的交互机制。

这里的原因是大部分脚本语言都是C/C++实现的,且多数宿主语言都了解这一点,所以都会做到尽可能地C语言友好。但这个总结不代表全部情况,还有一些其他场景。

这里最值得说一说的,是Go语言。

考虑到Go语言与C语言的友好性,上述的方案同样适用于Go语言。但是由于一方面Go对C语言的支持还有缺陷,另一方面Go也想取代C的地位,所以很多脚本语言的Go语言实现版本也雨后春笋般出现。

进程间的跨语言交互,本质上基于的是进程间交互机制,比较典型的代表是共享内存与socket。

12. 看啥看看不懂

原文提到:动态库本质上是实现了一个语言无关的代码复用机制。它是二进制级别的复用。 我理解是:无论是c语言,还是其他语言,都可以调用同一个动态库从而完成系统调用吗?

作者回复: 是这样

后续补充

...

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值