为什么C编译器这么少?


72

C是世界上使用最广泛的语言之一。它占现有代码的很大一部分,并继续用于大量的新代码。它受到用户的喜爱,它的广泛移植使能够运行C对许多平台的非正式定义而言,并因其是一种具有相对简洁功能的“小型”语言而受到其粉丝的称赞。

那么所有的编译器在哪里?

在桌面上,(实际上)有两个:GCC和Clang。考虑了几秒钟,您可能会记得英特尔也存在。还有很多其他的东西,对于普通人来说实在太晦涩难懂了,几乎没有普遍地去支持最新的语言版本(甚至常常是定义明确的语言子集,只是“子集”)。此列表的一半成员是历史脚注;其余大多数都是非常专业的,实际上还没有实现完整的语言。实际上很少有开源的。

Scheme和Forth-其他深受其迷们喜爱的小语言-可能比实际用户拥有更多的编译器。甚至像SML之类的东西,也要比C有更多“严肃”的实现方式可供选择。而针对验证的新的(未完成的)C编译器的发布实际上看到了一些相当负面的回应,而资深的实现则努力争取足够的贡献者甚至赶上C99。

为什么?实现C这么难吗?不是C ++。用户是否只是对其所属的复杂度组有一个非常歪斜的想法(即实际上它比Scheme更接近C ++)?


61
MSVC至少仍然可以算作C89编译器。甚至比英特尔更受欢迎。
Rufflewind

22
维基百科列出了很多C编译器。当您进入嵌入式领域时,它们会变得非常普遍。

113
您需要多少个编译器来编译C代码?
布赖恩·陈

76
这个问题是基于错误的前提。ADI,armcc,Bruce的C编译器,Bare-C交叉编译器,Borland编译器,clang编译器,Cosmic C编译器,CodeWarrior编译器,dokto编译器,Ericsson编译器,我什至不字母表的前五个字母呢。有一个疯狂大数量的C语言编译器。问题是:“如果我们不将这几十个算作真正的C编译器,为什么C编译器这么少?” 您已经将绝大多数C编译器定义为没有意思,这就是为什么它们很少的原因。
埃里克·利珀特

19
在大多数情况下,“为什么”问题对于本网站都是不好的问题,而“为什么不这样”呢?问题更糟。如果我在聚会上遇见您,问“那么,为什么不参加帆船比赛?” 我认为您应该正确地认为这是一个奇怪的问题。您无需为不从事技术上困难,身体冒险和非常昂贵的爱好提供理由。编写任何不平凡的软件都是昂贵,困难和冒险的,因此需要巨大的动力。一个更好的问题是“为什么会有那么多的C编译器?” 令人惊讶的是,不止一个。
埃里克·利珀特

Answers:


153

如今,您需要一个真正的C编译器作为优化编译器,尤其是因为C不再是一种与硬件接近的语言,因为当前的处理器非常复杂(乱序流水线超标量,具有复杂的缓存TLB,因此需要指令调度等)。即使今天的x86处理器都能够运行相同的机器代码,也不像上世纪的i386处理器那样。请参阅David Chisnall撰写的C语言不是低级语言(您的计算机不是快速的PDP-11)

很少有人在使用像tinyccnwcc这样的天真非优化C编译器,因为它们生成的代码比优化编译器所提供的代码慢几倍。

编码优化编译器很困难。请注意,GCC和Clang都在优化某些“源语言无关”的代码表示形式(GCC为Gimple,Clang为LLVM)。一个好的C编译器的复杂性不在解析阶段!

特别是,制作C ++编译器并不比制作C编译器难:解析C ++并将其转换为某些内部代码表示很复杂(因为C ++规范很复杂),但是众所周知,但是优化部分更加复杂复杂(在GCC内部:中端优化,源语言和目标处理器中立,构成了编译器的主要部分,其余部分在几种语言的前端和若干处理器的后端之间保持平衡)。因此,大多数优化的C编译器也能够编译其他一些语言,例如C ++,Fortran,D,... GCC的C ++特定部分大约占编译器的20%...

同样,C(或C ++)的使用如此广泛,即使人们没有完全遵循正式的标准(人们对语言的语义定义不够准确),人们也希望他们的代码是可编译的(因此每个编译器可能都有自己的解释方法)的)。还要查看CompCert证明的C编译器和Frama-C静态分析器,它们关心C的更正式语义

优化是一个长尾现象:实现一些简单的优化很容易,但是它们不会使编译器具有竞争力!您需要实现许多不同的优化,并巧妙地组织和组合它们,以获得具有竞争力的真实编译器。换句话说,现实世界中的优化编译器必须是复杂的软件。顺便说一句,GCC和Clang / LLVM都有几个内部专用的C / C ++代码生成器。两者都是巨大的野兽(数百万个源代码行,每年以百分之几的速度增长),拥有庞大的开发人员社区(数百人,大部分时间是全职或至少一半时间)。

请注意,有没有(据我所知)多线程的C编译器,即使一些编译器的部分可以并行运行(如程序内优化,寄存器分配,指令调度...)。与之并行构建make -j并不总是足够的(尤其是LTO)。

另外,很难从头开始为C编译器的编码提供资金,并且这种工作需要持续数年。最后,当今大多数C或C ++编译器都是自由软件(新兴公司不再有新的专有编译器市场,或者至少是垄断商品(如Microsoft Visual C ++)),并且几乎免费成为编译器所需要的软件(因为他们需要来自许多不同组织的捐款)。

我很高兴获得从头开始作为免费软件在C编译器上工作的资金,但是我还不够天真地相信今天有这种可能!


14
(there is no more a market for proprietary compilers告诉Visual Studio团队...
Mason Wheeler

18
微软拥有垄断地位。我的意思是,开发新的C编译器的小公司不会销售很多。您能说出MSVC最近的专有竞争对手吗?
Basile Starynkevitch 2015年

12
HPC世界中有许多专有的编译器。PGCC,NAG和ICC使用最广泛。
Davidmh,2015年

37
@MasonWheeler:如今,VS是免费赠送的(就像啤酒一样)。非免费版本添加了工具,但是VS2013中的C编译器在所有版本中都是相同的。就是没有市场,甚至没有市场。
MSalters 2015年

3
但是,GCC和LLVM都使用低得多的表示形式,并且它们同样对C ++和C(对于GCC为Ada和Fortran)代码进行了优化。相反,我想说C ++比C需要更多的优化(特别是在使用其STL编译代码时)!
Basile Starynkevitch 2015年

70

我想反驳您的基本假设,即只有少量的C实现。

我什至不了解C,不使用C,我也不是C社区的成员,但是,即使您所提到的少数几个编译器,我的知识也要多得多。

首先,最重要的是,该编译器可能完全使台式机上的GCC和Clang相形见Microsoft:Microsoft VisualC。尽管OSX和Linux都在台式机上取得了长足的进步,而iOS和Android的市场份额却“被盗”与以前的传统桌面用户不同,Windows仍然主要的桌面操作系统,大多数Windows桌面C程序可能都是使用Microsoft工具编译的。

传统上,每个OS供应商和每个芯片供应商都有自己的编译器。Microsoft作为OS供应商,具有Microsoft VisualC。IBM作为OS供应商和芯片供应商,都具有XLC(这是AIX的默认系统编译器,以及用于编译AIX和i / OS的编译器) 。英特尔拥有自己的编译器。Sun / Oracle在Sun Studio中拥有自己的编译器。

然后,有像PathScale和Portland Group这样的高性能编译器供应商,它们的编译器(和OpenMP库)用于数字处理。

Digital Mars仍在营业。我相信Walter Bright的独特之处在于,他是地球上唯一一个亲自(主要是)自己创建了生产质量C ++编译器的人。

最后但并非最不重要的一点是,我们拥有用于嵌入式微控制器的所有专有编译器。IIRC,在整个计算历史的总和中,每年售出的微控制器数量超过台式机,移动设备,服务器,工作站和大型机CPU。因此,这些绝对不是利基产品。

荣誉奖是TruffleC,它是使用Truffle AST解释器框架在JVM(!)上运行的C 解释器(!),其运行速度仅比GCC和Clang(在任何给定的基准测试中最快)低7%。计算机语言基准测试,并且比两者都更快。使用TruffleC,Truffle团队能够获得他们的JRuby + Truffle版本,以比实际的C Ruby实现更快地执行Ruby C扩展!

因此,除了您列出的实现之外,这还有6种实现,我什至不知道C就能知道。


1
在Microsoft Visual C之外,您提到的大多数C编译器都很少使用。
Basile Starynkevitch 2015年

6
MSVC是大型的C ++编译器,但是对于C语言来说,它很难使用,并且永久地停留在C89中。微控制器编译器通常是特定于目标的,卡在C89中并且很古怪;TruffleC似乎尚不可用(但很有趣,谢谢)。不过,Pathscale和Digital Mars似乎更像是我一直在寻找的反例。
Leushenko

8
@Mario我的意思不是C89坏了,但C89不是该语言的最新形式;并且,它意味着更少的编译器是跟上时代的存在。
Leushenko

6
@Leushenko MSVC不会永久卡在C89中。已经进行了一些讨论,应该添加更多的C99功能。首先,MSVC 2015起支持大多数C99库,并且还支持一些语言功能(不过主要是C ++ 11所需的功能)。
Morwenn 2015年

5
@Morwenn:Microsoft的政策似乎是C99不能解决C ++尚未解决的问题,并且,如果您正在进行系统编程,则应该使用C ++的类似C的子集(不需要运行时或您无法控制编译器将放置在何处的位置-如果您需要确保未从禁用分页的状态分页出代码或数据,则这一点很重要)。C99唯一的功能将是更高版本的C ++规范中所需要的功能,而这些功能都可以轻松实现。
Mike Dimmick

8

您需要多少个编译器?

如果它们具有不同的功能集,则会造成可移植性问题。如果它们被商品化,则可以选择“默认”(GCC,Clang或VS)。如果您关心最后5%的性能,则可以进行基准测试。

如果您是出于休闲目的或出于研究目的而进行编程语言工作,则可能使用的是更现代的语言。因此,用于Scheme和ML的玩具编译器激增。尽管OCaml似乎在非玩具非学术用途中获得了一定的吸引力。

请注意,这会因语言而异。Java本质上具有Sun / Oracle工具链和GNU。Python具有各种编译器,与标准解释器相比,这些编译器均未得到真正的尊重。Rust和Go分别只有一个实现。C#具有Microsoft和Mono。


1
显然,开发ML编译器还有更多有趣的理由……我只是认为C社区可能要大三个数量级才能平衡这种影响。但是你可能是正确的,1000 * 0仍然是0
Leushenko

创建新的编译器通常与社区的碎片联系在一起(无论是由原因引起的还是由原因引起的)。例如,egcs vs gcc维护者分裂。而且,C源兼容性通常低于100%。
pjc50

@ pjc50:基于诸如的基本类型,标准的有效编写方式将C细分为许多不相交的方言int,并且将需要不同的编译器以完全不同的方式解释相同的源代码。
2015年

5
我相信,Go有两种实现方式(在6g/ 8g/ ...工具链和gccgo)。过去也有一个非常有趣的专有商业实现,称为erGo,它是a)Go的本机Windows实现,当时gccgo或原始Go编译器都无法在Windows上很好地运行,b)长期押注Go的公司在它甚至成为1.0之前,以及c)用Go编写的Go的第一个实现(gccgo和6g / 8g都用C编写)。但是,在他们甚至退出封闭Beta之前,项目和公司都消失了。
约尔格W¯¯米塔格

6

C / C ++在编译语言中是唯一的,因为它具有3个主要规范的主要实现。

按照不使用任何不常用内容的规则,其他所有已编译语言都为0到1。

而且我认为javascript是您需要指定“已编译”的唯一原因。


2
标签“ C”适用于多种不同的语言。有些人将代码定义uint16_t a=48000u; unsigned uint32_t b=(a*a)/2;为分配b值8192。有些人将代码定义为分配值1152000000。如今,大多数人将其视为未定义行为,并且可能存储3299483648,但对此没有任何保证。
超级猫

1
@supercat:啊,一个很好的奇怪的东西,有溢出和整数提升规则。它取决于使用22u显然。
Zan Lynx

1
@ZanLynx:我不觉得有哪里2与2U任何情况下,合法的事项; 我知道唯一可能涉及2u和2u的未定义行为的情况。
2015年

3
@supercat:您将如何获得未定义的行为/2u?定义了无符号溢出(对于实现定义的N,取模2 ^ N),但除法甚至不会溢出。
MSalters

2
未定义行为将来自值的乘法,该值将被提升为signed int,但是其乘积不适合该类型。将结果强制为unsigned int可能会更改结果值的解释,但不会否定先前计算中的Undefined Behavior。
supercat 2015年

5

那么您的目标语言是什么?

SML编译器通常针对C或LLVM之类的东西(或在您的链接中看到的JVM或JavaScript)。

如果您正在编译C,则不是因为您要使用JVM。你要比C差。得多。然后,您需要为所有目标平台多次复制该次要地狱。

当然,C不是C ++,但我想说它比Scheme更接近C ++。它确实有其自己的未定义行为弊端的子集(我正在查看内置类型的大小)。而且,如果您弄糟了这些细节(或者“正确”但意外地做到了),那么您在重要系统上已有数十年的现有代码,可以告诉您您有多糟糕。如果您搞砸了SML编译器,它将无法正常工作- 可能会有人注意到。有一天


SML / NJ和PolyML都在编译为机器代码...
Basile Starynkevitch 2015年

2
int大小如何“未定义行为”?为何UB仍然是编译器供应商的负担?编译器编写者的唯一真正负担是int宽度是实现定义的,而不是未指定的,因此您必须记录所做的事情。
MSalters 2015年

@MSalters实际上,一个已建立平台的编译器编写者有责任与其他人相提并论。有时,这是经过记录和标准化的,有时则没有。查找int的大小很容易,但是查找寄存器值以及在调用函数时存储参数的位置(根据函数的参数类型和返回类型可能会改变),结构布局规则,等
Random832

@MSalters大多数人期望int它是32位或64位,但是它可以小到16位。产生一个超出范围的数字并不难,[−32767, +32767]并且int溢出是UB。还有char/ short是否提升为int unsigned int取决于是否int可以代表原始类型的每个值,如果操作数的类型不同且转换方式不同,则可以进一步触发从intunsigned int的转换,当您将结果分配给变量时,还可能转换为。
2015年

@MSalters标准类型的大小还有足够的回旋余地,我敢打赌,对于几乎所有非平凡的C程序,都可以选择合法的整数大小,这将导致它执行错误的操作或导致未定义行为。
2015年
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.