答题练手。手机排版不佳请谅解~
更新2:还是关于编译和解释
二者的本质区别是在编译/解释器的总体工作方式上的,编译器是off-line,解释器是on-line。编译器把整个程序读进来,进行一系列变大变小转化优化的过程,产生可执行文件,然后编译器退出,由可执行文件来读取和输出数据。解释器就像启动了一个打印机,不把纸和数据送进来它就一直闲置开着。解释器启动后相当于一个online服务,一有程序和数据进来就会经历类似编译的过程并直接产生结果,从这个意义上更接近于直接执行。
一种语言可能既可以编译,也可以解释,但一般来说可以解释的语言都可以编译。通常所说编译型语言和解释型语言应该是指它最常见的解析方式。
更新:关于编译型和解释型
解释型并非可以直接运行。所谓直接运行是使用REPL(Read-Eval-Print Loop,即读代码-求值-打印结果循环)或者虚拟机一键运行(如python <文件名>)带给你的假象。实际上GNU调试器GDB也可以实时执行C代码,但不能因此说C是解释型语言;Google的编译型语言Dart也可以直接dart <文件名>运行,然而人家要有main函数一次编译整个工程。
实际上python main.py这个命令会启动python虚拟机和解释器,将main.py的代码一行一行解释,只不过不同于REPL,它不会把每一行表达式的值打印出来。解释的过程也需要词法、语法分析和向目标代码(python字节码)的转化,也就是每一行都需要一个完整的编译过程(实际情况可能没这么简单)。类定义和函数定义会检查语法,但未定义的标识符不会报错,所以定义顺序不影响引用。python虚拟机实际执行的是编译完的字节码,而不是源代码。
对比C和Java,每次必须先编译(gcc, javac)再执行(./main, java Main),看上去比python更不"直接”,但区别真的不大。C之所以一经编译就无法跨平台,是因为他要编译到目标机器和操作系统的可执行文件。然而同样需要整个工程编译的Java,只拥有编译后的文件,就可以在装了兼容的JRE的各种平台运行。
不过,还真有可以直接运行的解释型语言。详情请查"Lisp Machine”。
原回答若有与此更新冲突的地方,以此更新为准。
=== 以下是原答案 ===
1. C 属编译型语言,Python 属解释型语言。
这点其他答主提过,确实是重要的区分点。编译型和解释型,在编程时的最大区别是必不必要写一个入口函数,在C语言里是 `main`,而 Python 可以不写。
编译型的优点是"静态”,代码不能一行一行编译执行,必须作为整个工程来编译,这样便于类型检查,降低运行时错误率;运行时效率更高,因为编译器可以统筹各个方面,生成更优化的机器指令;一经编译便可直接以机器语言再次执行。
解释型语言的优点是"动态”,代码的每一行可独立执行(代码块除外)。这样就可以灵活地进行实时交互,调整正在运行的程序,进行实时、异步的调试。比如 Python 的 CLI(命令行交互界面)就可以直接输入 Python 代码执行。这是 C 语言这样的编译语言难以做到的(调试模式下可以做到 C 表达式的实时计算,但达不到解释型语言的灵活度)。解释型语言的特性还让其适合做脚本语言。而且有一个东西是不支持解释的语言做不到的,就是eval,即将编译时未知的字符串转化为在原位置的代码执行。由于编译型语言在运行时无法编译代码,所以实现不了eval。 />
二者的缺点则互为对方占优的方面。编译型需要大量的编译时间,不够灵活。在大项目中,需求的更改可能导致巨大的重新编译时间。不过这被运行效率弥补,没有其他更大的缺点。而解释型语言被诟病得相对较多,比如因为不能事先编译,执行时都要先解释(本质上就是对单行的编译)成机器代码再执行,因此效率低很多。不过针对这点,Python 现在会像编译型语言一样把第一次执行时产生的字节码(虚拟机器代码)输出到 .pyc 文件,下次若源文件未改变,可直接执行字节码文件,从而提升了运行效率。另外,解释型语言难以实现静态类型检查,所以容易产生运行时错误,因此相对不适合用来做大项目。
其实非要让编译型语言也能支持命令行交互也并非不可能——Haskell就是这样一个例子。它不仅能单行执行,甚至还能做静态类型检查,这个会在后面提到。做到这一点必须精心设计语言。
2. C 是弱类型、静态类型检查的,Python 是强类型、动态类型检查的。
对于不了解的人来说,这种说法可能会引起质疑。来一条一条看。C 是弱类型?各种「类型不匹配」快要把我搞疯了,这还弱吗?"静态类型检查”又是什么鬼?
理解这件事情,要先知道 C 语言中类型的本质:一定长度的字节。C 中大多数基本类型都是某种长度的整数:int, short, long, long long, char,各种 unsigned,甚至指针也只是地址——机器字长度的整数而已,唯独 float 与 double 特殊。稍复杂点的,有数组和结构体,它们只是若干个整数或浮点数的组合。这种简单粗暴是因为 C 出生很早,那时冯诺依曼体系的高级语言大都与汇编很接近——汇编里没有类型,只有不同长度的字节。而 Python 中你是看不到 int, unsigned int, long 的区分的,因为它把不同长度的整数类型简化了,超出长度就自动扩展。
扯远了。知道 C 中的类型大体就是各种整数,就能想到各种整数可以互相转化:int 转化 long 很好理解,char 和 int 也经常互相转换,那你想过 int 能与任何一种指针强制转换吗?而这是源于 int 是最常用的整数类型,而地址也是最重要的机器整数。你还知道 void* 能与任何指针类型转换吗?其实所有的指针都可以任意互相转换,这意味着看似类型系统严格的 C 实际上并没有限制你去滥用它。这也不一定是滥用——库函数 malloc 的返回类型就是 void*,它表示你可以将它看作任何类型的指针。
那不同指针能相互转化又怎样呢?我们知道 Python 里的对象是一系列属性和方法的集合,这在 C 中类似于结构体。对象的名字相当于指向它在内存中实体的指针——相当于结构体指针。不同结构体指针可以任意转化意味着……内存中的对象可以被视为拥有任何类型,这不就是弱类型嘛!
那什么叫静态类型检查呢?对,就是那个让你饱受编译错误苦恼的东西。我说过编译型语言是"静态”的,所以静态类型检查只可能在编译型语言实现(前述,Haskell 是个杂糅者,它是编译型语言,但又可以单行执行)。C 要求你写出每个变量、函数的类型,就是为了类型检查。但并不是静态类型检查就一定要标注类型(如 Haskell 就几乎完全可以不写,得益于其类型推断系统,细写又是一篇论文)。Python "动态类型”,我可以理解,因为写的时候完全不用声明类型嘛。但你说它「强类型」?这我可不服了,哪个「强类型」语言可以随意给变量赋任意值的?
前面说过,储存对象的变量实际上只是储存对象在内存中的地址(而「值类型」,如小整数,一般直接存储值)。变量存储在「栈」中,对象被放在「堆」中。「栈」是一个个整齐叠放的,「堆」是随机开辟的。当我们执行 `a = [1,2,3]` 这样一条语句,Python 运行环境在「堆」中生成新的 list 对象,将对象的首地址返回,赋给新变量 `a` 压入栈中。之后若执行 `a = { 'x': 1 }`,只不过是将 `a` 中的地址换成字典对象的罢了。所以变量能储存任意对象不是因为变量本身能随意改变,而是指针可以指向任何东西。
而同样是指针能指向任何东西,Python 为何是强类型?因为 Python 的对象都携带了类型信息。如果你对整数和字符串做加法,它就会报这样的错误: />
这表明 Python 会对运行时的对象进行类型检查。这个其实还不够说明问题,因为加号不能用于 int 和 str 可能只是一种语义检查。那你可以想,当调用一个对象上不存在的方法会怎样,显然是会报错的。而更能说明问题的是这个: />
这表明 Python 的类型检查甚至比 C 更强,连 float 都不会给你隐式转化为 int。而这肯定不会是编译器行为,因为内置函数 chr 在声明时是不会标注参数类型的。尽管 3.7 版本允许类型声明,但 Python 不会以此为依据进行报错。另外,真正的弱类型语言 JavaScript 对上述整数加字符串、获取不存在的方法(调用会报错但获取不会)、整数参数传浮点数都不会报错。由此,你可以相信 Python 属于强类型语言了吧。
(补充:更彻底的弱类型语言比如 Lisp,甚至是在机器上实现弱类型的)
3. Python 是原生支持面向对象范式的语言,C 不是。
我没有说"Python 是面向对象语言,C 是面向过程语言”,因为面向对象和面向过程是编程模型和思想,Python 程序也能写得很"过程”,C 也有多种方法实现"面向对象”。但 Python 是原生支持面向对象范式的,它有 class 的封装,有继承、多态方法。C 要实现面向对象的特性,需要一些技巧。有一个知乎回答,我从它的第一个链接的 PDF 得到了很大启发。回答已经找不到了,但那个 PDF 还存着,书名叫《Object-Oriented ANSI-C》。
以上三点是两个语言最大的区别。其他还有很多,不过都不涉及编程范式的核心。包括但不限于:C 更偏向底层,抽象和语法糖更少,使用起来不如 Python 方便。但 C 更简单,如果有计算机体系的知识,精通起来比 Python 可能更容易。
Python支持lexical scope、闭包、嵌套函数和嵌套类,C没有闭包。
C 的语法不如 Python 简洁。
C 函数声明的先后顺序影响其可被调用的区域,Python 不会。
C 需要自己维护堆内存,Python 有垃圾回收机制。
C 的跨平台性不如 Python(对于使用这两个语言的程序员而言),因为 C 是大多数操作系统的实现语言,各操作系统会提供标准之外的系统调用库,而不同操作系统上系统库中的函数和定义不同。Python 基于虚拟机执行字节码,只要官方提供了某操作系统的虚拟机实现,就能将 Python 代码无缝在各操作系统转移(`os.system` 等涉及特定操作系统的东西除外)。(其实这也不算一个区别,C和Python的跨平台性都靠各平台的语言服务,不过C主要是平台自己给Python主要是Python官方给。真要说能编译运行C/Python的平台数量C要多很多。)
C 除预编译指令外可以压缩到一行,Python 要用游标卡尺(逃。
……