C语言这么厉害,它自身又是用什么语言写的?

探讨了C语言编译器的起源,解释了首个C编译器如何使用汇编语言编写,以及如何通过自举过程逐步完善C语言。

 这是来自我的星球的一个提问:“C语言本身用什么语言写的?”

换个角度来问,其实是:C语言在运行之前,得编译才行,那C语言的编译器从哪里来? 用什么语言来写的?如果是用C语言本身来写的,到底是先有蛋还是先有鸡?

1

我们假设世界上不存在任何编译器, 先从机器语言说起,看看怎么办。 

机器语言可以直接被CPU执行,不需要编译器。

然后是汇编语言, 汇编语言虽然只是机器语言的助记符,但是也需要编译成机器语言才能执行,没办法只能用机器语言来写这第一个编译器了(以后就不用了)。 

汇编语言的问题解决了,就往前迈进了一大步,这时候就可以用汇编语言去写C语言的编译器,我们说这是C编译器的老祖宗。 

有了这个老祖宗,就可以编译任意的C语言程序了,那是不是可以用C语言本身写一个编译器?只要用老祖宗编译一下就可以了。

OK, 这么一层层上来,终于得到了一个用C语言写的编译器, 真是够麻烦的。 

到这个时候,之前那个汇编写的C语言编译器就可以抛弃了。 

当然,如果在C语言之前,已经出现了别的高级语言,例如Pascal,那就可以用Pascal来写一个C语言的编译器。

第一个Pascal的编译器据说使用Fortran写的。而做为第一个高级语言的Fortran,它的编译器应该是汇编语言写的。

2

关于编译器,这里边有个有趣的传说:

传说Unix 发明人之一的 Ken Thompson在贝尔实验室,大摇大摆的走到任何一台Unix机器前,输入自己的用户名和密码,就能以root的方式登录! 

贝尔实验室人才济济,另外一些大牛发誓要把这个漏洞找出来,他们通读了Unix的C源码,终于找到了登录的后门, 清理后门以后编译Unix , 运行, 可是Thompson 还是能够登录进去。

有人觉得可能是编译器中有问题,在编译Unix的时候植入了后门, 于是他们又用C语言重新写了一个编译器,用新的编译器再次编译了Unix, 这下总算天下太平了吧。

可是仍然不管用, Thompson 依然可以用root登录,真是让人崩溃 !

后来Thompson 本人解开了秘密,是第一个C 语言编译器有问题, 这个编译器在编译Unix源码的时候,当然会植入后门, 这还不够,更牛的是,如果你用C 语言写了一个新编译器,肯定也需要编译成二进制代码啊,用什么来编译,只有用Thompson写的那第一个编译器来编译,好了, 你写的这个编译器就会被污染了,你的编译器再去编译Unix , 也会植入后门 :-)

说到这里我就想起了几年前的XcodeGhost 事件,简单来说就是在Xcode(非官方渠道下载的)中植入了木马,这样XCode编译出的ios app都被污染了,这些app就可以被黑客利用做非法之事。 

虽然这个XCodeGhost和Thompson的后面相比差得远,但是提醒我们,下载软件的时候要走正规渠道,从官方网站下载,认准网站的HTTPS标准,甚至可以验证一下checksum。

3

可能有人问:我用汇编写一段Hello World都很麻烦,居然有人可以用它写复杂的编译器?这可能吗?

当然可能,在开发第一代Unix的时候,连C语言都没有, Ken Thompson 和 Dennis Ritchie 可是用汇编一行行把Unix敲出来的。   WPS第一版是求伯君用汇编写出来的, Turbo Pascal 的编译器也是Anders 用汇编写出来的,大神们的能力不是普通人能想象得到的。 

对于编译器来说,还可以采用“滚雪球”的方式来开发:

还是以C语言为例,第一个版本可以先选择C语言的一个子集,例如只支持基本的数据类型,流程控制语句,函数调用...... 我们把这个子集称为C0。

然后用汇编语言写个编译器,只搞定这个语言的子集C0,这样写起来就容易不少。

C0这个语言可以工作了,然后我们扩展这个子集,例如添加struct,指针......  ,把新的语言称为C1。 

那C1这个语言的编译器由谁来写?   自然是C0。

等到C1可以工作了,再次扩展语言特性,用C1写编译器,得到C2。 

然后是C3, C4......  最后得到完整的C语言。

这个过程被称为bootstraping , 中文叫做自举。

END

我是刘欣,畅销书《码农翻身》作者,15年以上开发经验,前 IBM 架构师,领导过多个企业应用架构设计和开发工作;洞察技术本质,擅长用故事去讲解复杂技术。

每天仅需4毛钱, 加入码农翻身知识星球,和我以及来自腾讯、阿里、京东、滴滴、IBM、SAP等各路大牛深度交流技术学习,项目开发,编程技术,职业发展......

640?wx_fmt=png

<think>我们正在讨论C语言编译器是如何实现的。根据引用[3]和引用[4],我们可以知道编译器通常包括词法分析、语法分析、语义分析、中间代码生成、优化和目标代码生成等步骤。而关于编译器本身的实现语言,有一个著名的概念叫做“自举”(bootstrapping),即用某种语言语言的编译器。 对于C语言编译器,最著名的例子是GCC(GNU Compiler Collection)。根据引用[1],GCC是Linux下最常用的C语言编译器,它是用C语言本身编的。但是,这里有一个问题:第一个C语言编译器是用什么语言的?实际上,第一个C语言编译器(由Dennis Ritchie开发)是用汇编语言的。因为在那时,还没有C语言的编译器。然后,当这个汇编语言的C编译器能够编译C语言后,就可以用C语言来重这个编译器,并用已有的编译器(汇编语言的)来编译这个新编译器。这样,新的编译器就可以编译自己,这就是自举。因此,现代C语言编译器(如GCC)通常是用C语言的。但是,由于自举的过程,实际上在构建GCC时,我们需要一个已有的C编译器(通常称为“宿主编译器”)来编译GCC的源代码。这个宿主编译器可以是另一个C编译器(比如已经安装的旧版GCC,或者其他C编译器如Clang)。另外,引用[3]提到“自制C语言编译器”的实现过程,其中使用了C语言来实现。这也说明,我们可以用C语言来编一个C编译器。总结:1.第一个C语言编译器是用汇编语言的。2.现代C语言编译器(如GCC)通常是用C语言的,通过自举过程实现。3.在构建这些编译器时,需要一个已有的C编译器(宿主编译器)来编译它们的源代码。相关问题: 1.什么是编译器的自举(bootstrapping)? 2. GCC编译器的主要组成部分有哪些? 3.除了C语言,还有哪些编程语言可以用来实现C语言编译器?4.第一个C语言编译器是如何产生的?</think>### C语言编译器的实现语言 C语言编译器通常采用**自举(Bootstrapping)** 方式实现,其核心实现语言经历以下阶段: 1. **初始阶段:汇编语言** 最早的C语言编译器(如Ken Thompson和Dennis Ritchie开发的初版)是用**汇编语言**编的。因为当时不存在可用的C编译器,只能依赖底层汇编实现基础功能[^3][^4]。 2. **自举阶段:C语言自身** 当汇编版编译器具备基本功能后,开发者会**用C语言编译器代码**,并用已有的汇编版编译器编译新代码。例如: - **GCC**:当前版本完全用C/C++实现,但最初通过汇编编译器构建[^1] - **Clang**:基于C++实现,但构建过程依赖现有C编译器[^4] 3. **现代实现语言** - **C/C++**:主流编译器(GCC、LLVM/Clang)的核心部分使用C/C++,兼顾性能与控制力 - **其他语言**:部分实验性编译器可能使用Rust或OCaml,但需依赖现有C编译器构建[^3] > **关键过程示例**: > 假设用C语言新编译器`newcc`: > ```c > // newcc的C语言源码 > void compile(char* source) { > // 词法分析、语法树生成... > } > ``` > 用旧版汇编编译器`oldcc`(汇编编)编译`newcc`: > ```bash > $ oldcc newcc.c -o newcc # 生成第一版二进制 > ``` > 此后`newcc`可编译自身完成自举: > ```bash > $ newcc newcc.c -o newcc # 自编译升级 > ``` **技术挑战**: - **词法/语法分析**:需精确处理C语言标准(如ANSI C)[^1][^3] - **目标代码生成**:将AST转换为机器码,需深度理解CPU架构[^4] - **优化器实现**:涉及复杂算法(如寄存器分配、指令调度)[^4]
评论 81
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值