编程语言

我们都知道,计算机中的执行者 CPU 只认识二进制的 0 和 1 组成的序列,使用 0 和 1 组成的序列我们称之为 机器码机器码 是计算机可以直接执行的。但是直接使用机器码编写 CPU 指令不仅费时费力,还特别容易出错,于是后来有了 汇编语言。简单概括的话,汇编语言类似于一种助记符,直接面对指令,将二进制指令替换成人类便于记忆的字符串,并给定特殊的格式。每一条汇编指令对应一条二进制指令。根据内核架构的不同,不同的指令有不同的长度和格式。

汇编语言是一种 低级 编程语言,它更接近机器代码或二进制。因此,它们对人类来说更难阅读(尽管它们仍然比 1 和 0 更容易理解)。低级语言的好处是它们速度快,并且可以精确控制计算机的运行方式。
高级编程语言则更接近人类的交流方式。高级语言使用的单词(如对象、顺序、运行、类、请求等)更接近我们在日常生活中使用的单词。这意味着它们比低级编程语言更容易编程,尽管它们确实需要更多时间才能翻译成计算机的机器代码。随着计算机变得越来越强大,低级和高级编程语言之间的运行时间差异通常只有几毫秒。因此,高级语言在大多数情况下都可以解决问题。

现在我们学的 C/C++、Rust、Python、Java、Go 等都是高级语言,编写起来要简单很多。不过这些高级语言中人们又将其分类细化:编译型语言和解释型语言。

  • 编译语言的示例:C、C++、C#、CLEO、COBOL 等。
  • 解释语言的示例:JavaScript,Perl,Python,BASIC 等。

编译型语言顾名思义就是需要经过编译器的编译之后才能执行,只要程序通过编译,便可以由目标机器(Windows、Linux等)直接执行;而解释型语言是通过解释器直接运行的,读取一行内容便解释一行,指令不是由目标机器直接执行的。

一般而言,编译型语言通常更快,更节省内存,编译时会进行优化;而解释型语言通常较慢,且由于解释开销,内存使用较大。不过有的解释型语言的解释器实现、优化非常好,加上如今硬件的升级,性能与编译型语言不相上下。

特性 编译型语言 解释型语言
编译过程 需要编译步骤来生成可执行文件 无需编译,代码由解释器执行
编译时间 编译大型项目可能耗时较多 及时反馈,无需编译
依赖性 只需二进制文件和必要的库即可执行 需要安装解释器才能执行代码
可移植性 需要为不同平台重新编译 任何有解释器的平台都可以运行
执行速度 通常更快 通常较慢
内存使用 通常更节省内存,编译时会进行优化 由于解释开销,可能会占用更多内存
修改和测试 修改后需要重新编译,迭代较慢 修改后可直接测试,无需重新编译,迭代较快
示例 C,C++,Rust,Go Python、JavaScript、Ruby

上面的表格(以及下面的类型)来自:https://www.bilibili.com/video/BV1Y227YtETA

说起编译型语言就不得不提到编译器,传统的编译器通常分为三个部分,前端(frontEnd),优化器(Optimizer)和后端(backEnd)。在编译过程中,前端主要负责词法和语法分析,将源代码转化为抽象语法树;优化器则是在前端的基础上,对得到的中间代码进行优化,使代码更加高效;后端则是将已经优化的中间代码转化为针对各自平台的机器代码。常见的编译器有 GCC(GNU组织开发)、Clang、MSVC/Mingw 等。对了,要说明的是 Clang 的后端部分就是 LLVM

MinGW 和 MSVC 为 Windows C/C++ 语言编译支持。

编程语言的其它分类:静态、动态、强类型、弱类型

静态类型的语言:编译时就知道所有数据的类型,如果声明类型和赋予的值不匹配,IDE 便会报错。例如 C/C++、Rust、Java、Scala 等。

动态类型的语言:在运行程序的时候才知道数据的类型。例如 JavaScript、Python、PHP、Ruby 等。

强类型语言:对数据类型的赋值进行规则约束,通常使用强制转换。例如 Python、Rust、Java、 Ruby 等。

弱类型语言:对数据类型的赋值约束少/无约束,通常是隐式转换。例如 JavaScript、PHP、VB 等。

特性 静态 动态
类型检查 编译时 运行时
错误检测 早(执行前) 晚(执行期间)
代码灵活性
性能 通常更快 可能会更慢
示例 Rust、Java、C# Python、JavaScript
特性 强类型 弱类型
数据类型声明 显式、隐式 隐式
类型约束 严格 宽松
类型转换 需要显式进行 隐式进行
隐式转换 少见/罕见 常见
错误检查 多在编译时 多在运行时
类型安全性 高:较少出现意外行为 低:更容易出现不可预期的结果
示例 Rust、Java、Python PHP、JavaScript

网络协议

网络协议就是为计算机网络中进行数据交换而建立的规则、标准或约定的集合。例如,Linux 约定使用 UTF-8 字符集编码。

其它还有 tcp、udp、http、https、ssh、ftp、dhcp 等等等等。协议是一种大家共同遵守的规范,只要遵守了该规范就可以实现跨系统、跨主机、跨平台访问、传输,如果你一套、我一套,调用的时候层层套,甚至根本就不能通信!那么网络也不会发展为如今这样。

可以读读这篇文章:https://www.cnblogs.com/fzz9/p/8964513.html

硬件相关

中央处理器架构

比较常见是 x86、x86_64/x64/amd64、aarch/arm、risc-v、mips

架构 说明
x86 intel32 位处理器(其实更像是指令集)
ia32 Intel Architecture, 32-bit,缩写为IA-32,现在,IA-32一般又能引喻成所有的支持32位计算的 x86 架构。
x86_64/x64 intel64 位处理器,兼容 intel32 位处理器
amd64 AMD64 位处理器,和 intel64 位处理器相同,下载软件的时候看到 amd64 或者 x86_64 就是可以在 64 位处理器上运行的程序了
arm ARMv3 至 ARMv7 支持 32 位寻址空间,ARMv8-A开始支持 64 位寻址空间。aarch64 和 ARM64 都是指 64 位的 ARM 架构。
aarch 同上

1999年,AMD 公司首先在 IA-32 基础上,增加了 64 位寄存器,兼容早期的16位和32位软件系统,后来命名为 amd64。之后 intel 公司因为自身 IA-64(先于 amd64 研发,但是不兼容 IA-32,也不能直接运行在 x64 上,相当于是新的产品)的失败也接受了该方案,叫做 x86_64(Intel 的产品怎么能给 AMD 打广告呢)。操作系统厂商通常用 AMD64 或者 Intel64。
目前家用 PC 一般都是 x64,IA-64 价格昂贵,多用于服务器。

CISC(Complex Instruction Set Computer):复杂指令集计算机,使用包含微码的相对高级或复杂硬件来实现相对大量指令的计算机。每条指令可执行若干低级操作,如内存访问、算术运算或地址计算。在第一代 RISC 处理器设计出来之前,许多计算机架构师都在试图弥合 “语义鸿沟”–通过提供 "高级 "指令(如存储过程调用和返回)、循环指令(如 “递减和非零则分支”)和复杂寻址模式(允许将数据结构和数组访问编译成单条指令)来设计支持高级语言的指令集。

虽然这些架构达到了允许用更少的指令来表达高级语言结构的目的,但据观察,它们并不总是能提高性能。例如,在一种处理器上,人们发现不使用过程调用指令,而使用一系列更简单的指令,就有可能提高性能。此外,指令集越复杂,指令解码的开销就越大,包括执行时间和硅片面积。对于使用微码来解码(宏)指令的处理器来说,情况尤其如此。用微码实现的复杂指令集比用硅片 "硬连接 "解码的指令集更容易调试。CISC 处理器的例子是摩托罗拉 680x0 家族和英特尔 80186 到英特尔 486 和奔腾。

RISC(Reduced Instruction Set Computer):精简指令集计算机,其设计基于简单指令序列的快速执行,而不是基于提供大量复杂指令。

RISC设计中通常具有的特征是统一的指令编码(例如,操作码始终位于每个指令的相同位位置,该指令始终为一个字长),这允许更快的解码;非同构寄存器集,允许在任何上下文中使用任何寄存器,简化编译器设计;以及具有更复杂模式的简单寻址模式,被简单的算术指令序列所取代。
使用 RISC 处理器(或多或少)的例子有 Berkeley RISC、HP-PA、Clipper、i960、AMD 29000、MIPS R2000 和 DEC Alpha。IBM 的第一台 RISC 计算机是 RT/PC (IBM 801),现在他们生产基于 RISC 的 RISC System/6000 和 SP/2 系列。

内存堆栈

栈:存储局部变量,大小在编译时已知,具有良好的内存局部性;
堆:存储动态分配的对象,大小在运行时确定,具有较高的灵活性;

所有分配在堆上的数据,都是通过一个在栈上的引用来访问的。

我们编写的程序都是需要占用内存来运行的。

元编程

元编程这个词看似很新,比较陌生,其实日常开发中经常使用,编程语言也都提供了元编程的一些 API。

比如 C/C++ 的宏,Java 的反射啊、注解啊以及 Java 的日常项目中使用的 Lombok 等类似的东西。这些东西极大的提高了程序员的效率并且简化了开发,非常的好用。例如 Java/Kotlin 大多使用以下方式进行元编程中的符号处理。

  • Java: Annotation Processing Tool(APT)
  • Kotlin: Kotlin Symbol Processing(KSP)

在 Java 中,如果你使用过 SPI,那么就知道 APT 和 KSP 的处理器都是通过 ServiceLoader 加载的。浪子在之前的 Java API 一文中提过 ServiceLoader,好像是获取某个接口的所有实现类。在 Java 的许多框架中都使用了 ServiceLoader。Java 说的有点儿多了,哈哈哈,毕竟是老本行。


本站由 江湖浪子 使用 Stellar 1.29.1 主题创建。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。