|
|
0.
因为计算机只能识别0和1,这样的二进制数
1.
机器语言,机器码,cpu唯一认识的语言是机器语言,.exe文件就是cpu可直接执行的文件,但是机器码和CPU架构是深度绑定的,也就是说,.exe文件不能跨平台执行
2.
编译,将c语言编译成汇编语言,将助记符(汇编语言)编译成机器码,接下来cpu就可以执行了
3.
编译型语言将源代码一次性编译生成一个可执行文件,里面存有可直接被cpu执行的机器指令,要执行代码,就运行最后的可执行文件,因此对已经完成编译的代码,执行起来的速度很快,效率很高;
解释型语言是将源代码先编译成字节码文件,里面并没有机器指令,当需要执行代码时,再由解释器把字节码转换成cpu可直接执行的机器语言,并交给cpu执行。这样做的好处在于,字节码文件只需要对应的解释器就可以被翻译成cpu可直接执行的机器码,所以在有解释器的情况下,字节码文件可以跨平台执行,而这时可执行文件做不到的,
所以二者的本质区别是,编译型语言经过编译产生可含有机器码的,可被cpu直接执行的可执行文件,解释型语言经过编译产生含有字节码的,须经解释器翻译才可被cpu执行的字节码文件
下面是把你两轮的思考整合后的完整版,可以直接作为作业答案交,也可以按老师要求删掉第五节(那是加分项,有些教材口径下不必写)。
一、编译型语言
编译型语言在程序运行之前,由编译器将源代码一次性编译(预处理 → 编译 → 汇编 → 链接)生成一个可执行文件,其中存放的是可被 CPU 直接执行的机器指令。执行时直接运行这个可执行文件,不再需要翻译过程,因此执行速度快、效率高。
需要注意的是,这里的"一次性"是相对于解释型"边执行边翻译"而言的——编译本身包含多个阶段,但它是运行前一次性完成的,不是反复进行的。
二、解释型语言
解释型语言在程序运行时,由解释器逐条读取源代码并翻译成机器指令交给 CPU 执行,即"边翻译边执行",不生成能够脱离解释器独立运行的机器码文件。
这里需要注意,解释型语言有两种实现方式:
• 纯解释型:解释器直接读取源代码,逐行翻译一条执行一条,不产生任何中间文件(如 Shell 脚本、早期 BASIC);
• 字节码 + 虚拟机:先将源代码编译成字节码,再由虚拟机逐条解释执行(如 Java 的 .class、Python 的 .pyc)。
因此,"先编译成字节码"只是后一种实现的特征,不能概括所有解释型语言。解释型语言真正的普遍特征是:翻译发生在运行时,不产生可独立执行的机器码文件。
三、跨平台性的来源
字节码文件之所以能跨平台,是因为翻译被推迟到了运行时:只要目标平台上装有对应的解释器/虚拟机,运行时由它把字节码翻译成该平台的机器码即可。
而可执行文件做不到这一点——其中的机器码与特定的 CPU 指令集和操作系统 ABI 绑定,换一个平台就需要重新编译。
四、本质区别
二者的本质区别不在"产出什么文件"(这是结果),而在翻译的时机与方式:
• 编译型:运行前一次性整体翻译为机器码,运行时直接执行;
• 解释型:运行时边翻译边执行。
进一步说,这背后是抽象层次的不同:
• 编译型的源码面向的是真实的物理机器(具体 CPU 指令集 + 具体操作系统接口),所以一旦落地为机器码,就必然与这台具体机器绑定;
• 字节码面向的是一台抽象的虚拟机器(JVM 规范、CPython 字节码规范),它的规范是统一定义的,不随硬件变化。
平台无关性并不是因为"文件里没有机器码",而是因为文件里的代码根本不是写给任何一台真实机器的。 至于虚拟机如何把它变成真实机器码,那是虚拟机自己的事。
五、深入:为什么不直接分发源码各自编译?(补充)
既然跨平台是相对可执行文件而言的,理论上编译型语言也可以通过"分发源代码、各平台分别编译"(即从源码构建)来实现跨平台。这条路确实可行,Linux 的 Gentoo、Arch 的 AUR、Python 的 C 扩展都是这么做的。但它之所以没能成为主流,是因为代价太高:
1. 编译型语言的源码本身并不天然跨平台。 C/C++ 直接暴露底层,Windows 用 CreateThread 而 Linux 用 pthread_create,文件路径分隔符、动态库机制(.dll / .so / .dylib)、字长与内存对齐在不同平台都不一样,需要程序员用条件编译主动维护适配。"写一次,到处编译"(WOCA)的承诺,比"写一次,到处运行"(WORA)弱得多。
2. 用户门槛和时间成本被转嫁给了每个终端用户。 装编译器、配环境、解决依赖冲突,而 Firefox、Chromium 这类项目从源码编译要数小时和数十 GB 资源;相比之下字节码的运行时翻译是毫秒级、用户无感知的。
3. 有些平台根本没有编译器。 游戏主机、嵌入式设备、浏览器沙箱中无法在目标机上编译。
4. 闭源商业软件不提供源码,此时可执行文件的平台依赖性必然要被解决。
根本原因是成本结构不同。 假设有 N 个程序、M 个平台:编译型路线每个程序在每个平台都要单独编译和适配,工作量是 N × M,且每个格子里的平台坑都要各自踩一遍;虚拟机路线把与具体平台相关的翻译工作一次性做进 M 个平台的虚拟机中,再让 N 个程序直接运行,总工作量是 N + M,省掉了巨大的交叉项,且虚拟机的维护成果可被所有程序复用。
结论:跨平台性不是靠"消灭平台差异"实现的,而是靠"把平台差异收敛到一个可复用的层里"实现的。
六、补充说明
现代语言的界限已经模糊:Java 的 JIT、JavaScript 的 V8 引擎都会在运行时将热点代码编译为机器码。因此"编译型/解释型"更多是在描述实现方式的倾向,而非语言与生俱来的属性。
和原答案相比,主要修正了三点:① "不断编译"改为"运行前一次性编译";② 不能说解释型语言"都要先编译成字节码",纯解释型并不生成中间文件;③ 本质区别从"产出什么文件"改为"翻译的时机与方式",并补充了抽象层次这一更深层的原因
4.
不能。
假设两台机器使用的都是 x86_64 指令集架构,Linux 下用 C 语言编译出的可执行文件仍然无法在 Windows 上运行,原因有三层:
可执行文件格式不同:Linux 生成的是 ELF 文件,Windows 生成的是 PE 文件。Windows 的加载器只能识别 PE 格式,遇到 ELF 文件会直接判定为无效映像,无法完成加载,指令根本不会被交给 CPU 执行。
系统调用接口不同:C 程序的文件读写、内存申请等操作最终都要进入内核,Linux 使用 Linux 的系统调用号,Windows 使用 Win32 API / ntdll,两者的编号和参数约定完全不同。
依赖的动态库不同:Linux 程序默认动态链接 glibc 等库,Windows 上没有这些库。
因此,CPU 指令集相同只是必要条件,而非充分条件。机器码能被 CPU 识别,只解决了最低一层的问题;在此之上,可执行文件格式、操作系统 ABI 与系统调用接口、运行时库的差异,同样会阻止程序跨平台运行。
5.
解释型语言的源代码文件运行时是被解释器翻译成可被cpu识别的指令,再逐条执行的,不生成可脱离解释器独立运行的机器码文件,平台相关性并没有消失,而是被转移并集中到了解释器/虚拟机里,解释器是平台相关的原生程序:x86-64 Windows 有一个版本,ARM64 macOS 有另一个版本;
CPU 指令集、可执行文件格式、系统调用接口、字符编码、文件路径这些全部差异,都由各平台各自的解释器版本吸收掉了;
而标准库进一步把文件、网络、进程等 OS API 封装成统一接口(open()、os.path.join());
于是你的代码面对的只是一台抽象的虚拟机,它"不是写给任何一台真实机器的"。
因此只要平台上有解释器和源码,就可以被执行
6.
人类通过语言文字来指代事物,无论是抽象的还是具体的,因此人类的表达非常丰富,摩斯密码应该是符号的符号,它通过一套符号来指代文字,相当于对具体事物进行了两次的抽象,以此适配实际的信息传输通道,从纸笔文字,变成了电报机与电流持续时间,
摩斯密码的原理并非保密,而是编码(channel encoding):​ 它把文字字符(字母、数字、标点)按照一张公开的码表,映射为仅由两种基本符号——"点"(短信号)和"划"(长信号)——加上不同长度的间隔所构成的时序序列。
其本质目的是适配信道:电报机等早期通信设备只能表达"有/无电流"两种状态(一个开关),文字无法直接在这样的信道上传输,因此必须先被"降维"成通断时序信号。由于这种表示只依赖"长短"与"有无",它与具体传输介质无关——同一份摩斯码既可用电流、也可用灯光闪烁、哨音长短或敲击来传递,实现"一次编码,跨介质传输"。
需要强调的是,摩斯密码是编码而非加密:码表是公开的 ITU 国际标准,任何掌握码表的人都能解码,它对内行毫无机密性;"未学习者看不懂"只是知识门槛的副产品,而非设计目的。这也要求通信双方必须约定同一套码表,否则解码失败。
此外,摩斯码为高频字符分配最短编码(如 E 为 .,T 为 -),是按使用频率优化传输效率的早期实践,体现了编码方案中"表示长度与传输效率"的权衡。
7.
亲们,趁敌人吃饭时发动进攻
动动手:
|
|