C编程语言问世时是否被视为低级语言?


151

当前C被认为是一种低级语言,但是在70年代,它被认为是低级语言吗?那么,该术语是否还在使用?

许多流行的高级语言直到80年代中期及以后才出现,所以我很好奇这些年来低级语言的性质是否以及如何发生了变化。


13
作为一个数据点,大约在1978年,一些编程指导员向我描述了C作为一种出色的汇编语言。
Ben Crowell '18年

7
@BenCrowell我不确定在您的声明中“荣耀”是什么意思,但是我已经经历过将C称为通用(独立于平台)的汇编语言
Melebius

13
有趣的是,还有待作出的情况下,C不是一个低层次的语言,并且是,现在一个比它在70年代,因为C的抽象机是远离现代硬件取出比它从PDP-11。
汤姆(Tom)

5
考虑到这一点,您可能还想考虑起源-Unix是用C编写的,c在Unix上运行,并将unix交叉编译到其他平台。编译Unix不需要做的任何事情都是不必要的开销,C的主要目标是使编写/移植编译器变得尽可能容易。为此,它是EXACT正确的级别,所以我不认为C是高级别还是低级别,我认为它是移植Unix的工具,就像几乎所有Unix工具一样,它非常适应许多问题。
比尔K

4
值得一提的是,口齿不清是在发明了1958年
总理

Answers:


144

要回答该问题的历史方面:

Brian Kernighan和C设计师Dennis Ritchie编写的《 C编程语言》(您可能听说过的“ K&R” )中解释了该设计原理。第一版的序言说

C不是一种“非常高级”的语言,也不是一种“大”的语言。

引言说

C是一种相对“低级”的语言... C没有提供直接处理复合对象(例如字符串,集合,列表或数组)的操作。没有可以操作整个数组或字符串的操作...

在文本继续之前,该列表会持续一段时间:

尽管缺少其中某些功能似乎是一个严重的缺陷,但是……将语言缩小到适中的大小确实有好处。

(我只有1988年的第二版,但是下面的注释表明所引用的文本与1978年的第一版相同。)

因此,是的,当时使用的术语是“高电平”和“低电平”,但是C被设计为介于两者之间。可以用C语言编写可跨硬件平台移植的代码,这是当时是否将该语言视为高级语言的主要标准。但是,C缺少一些高级语言所特有的功能,因此这是设计的一种简化决定。


42
这是一个很好的答案!可操作的历史证据+演员的证词,在《任择议定书》所指的时期内,能最好地了解低和高水平的含义。顺便说一句,我确认这些报价已经在1978年版中了。
Christophe

157

这取决于您对高级和低级语言的定义。在开发C时,任何比汇编语言都高级的语言都被视为高级语言。这是一个需要清除的低门槛。后来,该术语转移到了如今有些人甚至将Java都视为低级语言的地步。

即使在70年代的高级语言环境中,也要指出C是相当低的水平。C语言基本上是B加一个简单的类型系统,并且B只是方便的用于汇编的过程/结构化语法层。由于类型系统是对无类型B语言的改型,因此您仍然可以在某些地方省略类型注释,并且int将被假定。

C有意识地省去了当时已经建立的昂贵或难以实现的功能,例如

  • 自动内存管理
  • 嵌套函数或闭包
  • 基本的OOP或协程
  • 更具表现力的类型系统(例如,范围受限制的类型,用户定义的类型,例如记录类型,强类型,等等)

C确实具有一些有趣的功能:

  • 支持递归(由于其基于堆栈的自动变量,与所有变量都具有全局生存期的语言相比)
  • 函数指针
  • 用户定义的数据类型(结构和联合)是在C的初始版本发布后不久实现的。
  • C的字符串表示形式(指针到字符)实际上是对B的巨大改进,B将多个字母编码为一个机器字。
  • C的头文件是一个高效的技巧,可以减小编译单元的体积,但同时也提供了一个简单的模块系统。
  • 与更安全的引用相比,汇编样式的无限制指针和指针算术。指针本质上是不安全的,但对于底层编程也非常有用。

在开发C的时候,其他创新语言,例如COBOL,Lisp,ALGOL(各种方言),PL / I,SNOBL,Simula和Pascal已被发布和/或广泛用于特定的问题领域。但是,这些现有的语言中大多数都是用于大型机编程的,或者是学术研究项目。例如,当ALGOL-60最初被设计为通用编程语言时,实现它所需的技术和计算机科学还不存在。其中一些(某些ALGOL方言,PL / I,Pascal)也旨在用于低级编程,但它们往往具有更复杂的编译器或过于安全(例如,没有无限制的指针)。Pascal特别缺乏对可变长度数组的良好支持。

与那些语言相比,C拒绝了“优雅”且昂贵的功能,以便对低级开发更加实用。C从来不是一个主要的语言设计研究项目。相反,它是在资源有限的PDP-11小型计算机上Unix内核开发的分支。对于其利基市场(使用易于移植的单遍编译器编写Unix的极简主义低级语言),C绝对出色–超过45年后,它仍然是系统编程的通用语言


17
对于不了解现有ALGOL家族和Pascal语言需要什么的人来说,这只是一小部分:这些语言具有词法嵌套的函数,您可以在其中访问外部函数中声明的(局部)变量。这意味着要么必须在每个函数调用和返回(更改了词法级别)时维护“显示”(指向外部词法作用域的指针数组),要么必须将词法作用域链接到堆栈以及每个此类变量访问需要在堆栈上进行多次间接跳转才能找到它。昂贵!C抛弃了所有这些。我仍然想念它。
davidbak

12
@davidbak:x86-64系统V ABI(又称Linux / OS X上的调用约定)定义%r10为“静态链指针”,这正是您在说的。对于C来说,这只是另一个调用过多的暂存器,但我想Pascal会使用它。(当此类函数未内联时,GNU C嵌套函数将其用于将指针传递给外部范围(例如,如果创建指向它的函数指针,以便编译器在堆栈上创建机器代码的蹦床):常规的可接受性r10和r11的使用
彼得·科德斯

2
@PeterCordes着迷!当System V发布时,Pascal仍被广泛使用(尽管我不知道何时定义正式的SysV ABI)。您的链接答案非常有用。
davidbak

3
C具有用户定义的类型(结构/联合)。我怀疑其余的这些“功能”被忽略了,因为它们的使用为零或否定(除非您参加混淆的代码竞赛:-),因为它们不利于保持语言既简单又富有表现力的目标。 。
jamesqf

6
@jamesqf:但是很早的C就没有结构分配;我想您必须单独进行memcpy或复制成员,而不是a = b;像在ISO C89中那样编写复制整个结构。因此,在早期的C语言中,用户定义的类型绝对是第二类,只能通过引用作为函数args传递。 C对数组的厌恶,以及 为什么C ++支持在结构中对数组进行成员分配,但通常不支持?
彼得·科德斯

37

在1970年代初期,使用现代结构有效地使C充满了新鲜的空气,以至于整个UNIX系统可以从汇编语言重写为C,而空间或性能的损失却微不足道。当时,许多当代人将其称为高级语言。

C的作者,主要是Dennis Ritchie,更为谨慎,在《贝尔系统技术期刊》上的文章中说:“ C不是一种非常高级的语言。” 丹尼斯·里奇带着一丝不苟的笑容,想要挑衅,会说这是一种低级的语言。他对C的设计目标中的首要目标是使语言与机器保持紧密联系,同时提供可移植性,即机器独立性。

有关更多信息,请参阅原始的BSTJ文章:

谢谢丹尼斯。愿你安息。


3
如果您问我,它基本上是用于PDP组装的类型化和语法更好的包装器。
einpoklum


2
@einpoklum我倾向于同意这一点。大约在1980年,我在大学时就通过PDP-11 / 34a学习了C语言,一位教授将其描述为“便携式汇编语言”。可能是因为除了PDP-11外,我们在实验室中还拥有几台Superbrain CP / M机器,这些机器上都提供了C编译器。 en.wikipedia.org/wiki/Intertec_Superbrain
dgnuff

2
基于可验证的历史证据,这也是一个很好的答案(哇!那里有K&R的预版本!)。
Christophe

7
@einpoklum这有点牵强吗?我有机会在80年代中期用汇编器(Z80和M68K),一个名为M(带有抽象16位寄存器和指令集的PDP11语法)和C的“便携式汇编器”和C编写系统和应用程序代码。 ,C绝对是一种高级语言:C编程的效率比汇编程序高出一个数量级!当然,没有字符串(SNOBOL),没有本机数据文件支持(COBOL),没有自生成代码(LISP),没有高级数学(APL),没有对象(SIMULA),所以我们可以同意它不是很高
Christophe

21

正如我在该网站上的其他地方所写的当有人将malloc / free内存管理模式称为“低级编程”时,

有趣的是,“低级”的定义如何随时间变化。当我第一次学习编程时,提供标准化堆模型(使简单的分配/释放模式成为可能)的任何语言实际上都被认为是高级的。在低级编程中,您必须自己跟踪内存(不是分配,而是内存位置本身!),或者如果您真的很喜欢,请编写自己的堆分配器。

就上下文而言,这是在90年代初期,即C语言问世之后。


自80年代末标准化以来(不是基于现有的Unix API),标准库不是唯一的东西吗?同样,内核编程(这是C的原始问题域)自然需要诸如手动内存管理之类的低级内容。当时,C一定是认真的内核所用的最高级别的编程语言(我认为如今NT内核也使用了大量的C ++)。
阿蒙(Amon)

6
@hyde,我使用Algol 60,两种不同的FORTRAN方言和1970年代的几种不同的BASIC方言,这些语言都没有指针或堆分配器。
所罗门慢

7
严格来说,您仍然可以malloc()直接通过自己调用brk(2)mmap(2)管理生成的内存而无需编程。这是一个巨大的PITA,没有任何明显的好处(除非您碰巧实现了类似malloc的事情),但是您可以做到。
凯文

1
@amon-除了出色的基于Burroughs堆栈的计算机,这些计算机是在ALGOL中从下到上编程的,而且比Unix早得多。哦,顺便说一下,Multics,它是Unix的灵感:用PL / I编写。类似ALGOL,比C更高的层次
davidbak

6
实际上,今天我们更多人不使用Multics的原因可能与它只能在非常昂贵的大型机上运行有关,即,当日典型的大型主机费用加上半个机柜的额外费用。专用硬件以实现具有安全性的虚拟内存。当诸如VAX-11之类的32位微型计算机问世时,除了银行和政府之外,所有人都从IBM和七个小矮人中撤出,并将其大规模处理转移到“微型”计算机上。
davidbak

15

许多答案已经提到早期文章,这些文章都说“ C不是高级语言”之类的东西。

但是我无法抗拒:当时有很多(如果不是全部或全部)HLL-Algol,Algol-60,PL / 1,Pascal-提供了数组边界检查和数字溢出检测。

最后,我检查了缓冲区,整数溢出是许多安全漏洞的根本原因。是的,仍然如此

动态内存管理的情况更为复杂,但是,就安全性而言,C风格的malloc / free仍然是一大步。

因此,如果您对HLL的定义包括“自动防止许多低级错误”,那么,如果没有发生C和UNIX,网络安全的遗憾状态将大为不同,可能更好。


7
由于我碰巧参与了英特尔MPX的实现以及是否编译出来的指针/边界检查编译器,因此,我可以向您介绍有关其性能的论文:本质上为5-15%。其中大部分涉及到在1970年代和1980年代几乎不可能进行的编译器分析-与天真的检查相比可能要慢50%。但是,我认为可以说C和UNIX在20年之前就推迟了此类分析工作-当C成为最流行的编程语言时,对安全性的需求就大大减少了。
Krazy Glew '18

9
@jamesqf此外,C之前的许多机器都对边界检查和整数溢出提供了特殊的硬件支持。由于C没有使用该硬件,因此最终弃用并删除了它。
Krazy Glew '18

5
@jamesqf例如:MIPS RISC ISA最初基于斯坦福基准测试,该基准最初是用Pascal编写的,但后来才移至C。由于Pascal检查有符号整数溢出,因此MIPS也在ADD等指令中进行了检查。大约在2010年,我在MIPS工作,我的老板想删除MIPSr6中未使用的指令,研究表明,几乎从未使用过溢出检查。但事实证明Javascript进行了此类检查-但由于缺乏操作系统支持而无法使用便宜的指令。
Krazy Glew '18

3
@KrazyGlew-提出这一点非常有趣,因为在C / C ++中,由于有符号整数溢出,用户和编译器作者之间针对“未定义行为”的争执正在升温,因为今天的编译器作者秉承了古老的口头禅“做错了事”程序更糟是没有罪”,并将其提高到11。大量关于stackoverflow和其他地方的帖子反映了这一点……
davidbak

2
如果Rust具有像C这样的SIMD内在函数,它可能是近乎理想的现代便携式汇编语言。由于积极的基于UB的优化以及无法移植地公开现代CPU支持的新原始操作(例如popcnt,计数前导/尾随零,位反转,字节反转,饱和),C作为一种便携式汇编语言变得越来越差。数学)。要使C编译器在具有硬件支持的CPU上为它们进行高效的汇编,通常需要不可移植的内在函数。让编译器在没有目标的情况下在目标上模拟popcnt优于get的成语识别popcnt
彼得·科德斯

8

考虑早于C(1972)的旧语言和高级语言:

Fortran-1957年(不比C高很多)

Lisp-1958年

Cobol-1959年

Fortran IV-1961年(级别不比C高很多)

PL / 1-1964年

APL-1966

再加上类似RPG(1959)的中级语言,主要是一种编程语言,以取代基于插件的单位记录系统。

从这个角度来看,C似乎是一种非常底层的语言,仅比当时大型机上使用的宏汇编程序高一点。在IBM大型机的情况下,汇编程序宏用于诸如BDAM(基本磁盘访问方法)之类的数据库访问,因为当时数据库接口尚未移植到Cobol,这导致了汇编和混合的遗留问题。今天,IBM大型机上仍在使用Cobol程序。


2
如果要列出较旧的较高语言,请不要忘记LISP
Deduplicator

@Deduplicator-我将其添加到列表中。我一直专注于在IBM大型机上使用的软件,我不记得LISP这么流行,但是APL还是IBM大型机(通过分时控制台)和IBM 1130的利基语言。类似于LISP,APL是其中之一例如,使用更独特的高级语言查看使用当前APL版本创建Conway的生活游戏所需的代码很少:youtube.com/watch?v = a9xAKttWgP4
rcgldr

2
我不会把RPG或COBOL视为特别高级,至少在COBOL-85之前是这样。刮擦COBOL的表面时,您会发现它本质上是非常高级的汇编程序宏的集合。首先,它缺少功能,过程和递归,以及任何形式的作用域。所有存储都必须在程序的顶部声明,否则将导致极其长的序曲或痛苦的变量重用。
idrougge

我使用Fortran IV标准的一些不好的回忆,我不记得它是明显“更上一层楼”比C
DaveG

@idrougge-COBOL和RPG,以及大型机指令集包括对打包或未打包BCD的完全支持,这是美国等国家/地区对财务软件的要求。我认为诸如“移动对应”之类的相关本机运算符是高级的。RPG的与众不同之处在于,您指定了原始输入字段与格式化输出字段和/或累加器之间的链接,但没有指定操作顺序,与其替换的插件编程类似。
rcgldr

6

您问题的答案取决于它询问的是哪种C语言。

Dennis Ritchie的1974 C参考手册中描述的语言是一种低级语言,它为高级语言提供了一些编程上的便利。从该语言衍生的方言同样倾向于是低级编程语言。

但是,当1989/1990 C标准发布时,它并没有描述在编程实际机器时很流行的低级语言,而是描述了一种高级语言,该语言可以是-但不是必须的- -以较低级别的术语实现。

正如C标准的作者所指出的,使该语言有用的原因之一是,许多实现都可以被视为高级汇编器。由于C还被用作其他高级语言的替代,并且由于许多应用程序不需要执行高级语言无法完成的功能,因此该标准的作者允许实现以任意方式运行如果程序尝试使用低级构造。因此,C标准描述的语言从来都不是低级编程语言。

要了解这种区别,请考虑Ritchie的Language和C89如何查看代码片段:

struct foo { int x,y; float z; } *p;
...
p[3].y+=1;

在“ char”为8位,“ int”为16位big-endian,“ float”为32位且结构上没有特殊填充或对齐要求的平台上,因此“ struct foo”的大小为8个字节。

在Ritchie的语言中,最后一条语句的行为将采用存储在“ p”中的地址,向其添加3 * 8 + 2 [即26]个字节,然后从该地址处的字节中获取16位值,然后再取下一个,在该值上加一个,然后将该16位值写回相同的两个字节。该行为将被定义为对地址p处的第26和27个字节进行操作,而不考虑存储在此处的对象的种类。

在C标准定义的语言中,如果* p标识“ struct foo []”的元素,然后至少还有三个该类型的完整元素,则最后一条语句将为的成员y添加一个* p之后的第三个元素。在任何其他情况下,行为都不会由标准定义。

Ritchie的语言是一种低级编程语言,因为尽管它允许程序员在方便时使用数组和结构之类的抽象,但它根据对象在内存中的底层布局定义了行为。相比之下,C89和更高版本标准描述的语言以更高级别的抽象来定义事物,并且仅定义与此一致的代码的行为。适用于低级编程的高质量实现在比标准所要求的更多的情况下将发挥更大的作用,但是没有“官方”文档指定实现必须适合这种目的的措施。

因此,由丹尼斯·里奇(Dennis Ritchie)发明的C语言是一种低级语言,因此被认为是这种语言。但是,在缺乏实施提供的超出标准职责范围的保证的情况下,C标准委员会发明的语言从来都不是低级语言。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.