C语言代码编译后是什么?

问个比较二的问题,C语言代码编译后是什么代码?记得以前看到说是机器码。可是如果是cpu能直接执行的代码那还要操作系统干嘛?岂不是不同平台的程序都能直接用了?
---------------------------
看了后面的争论那现在再补充下问题,就是C语言代码编译后的代码是能直接操作机器的代码还是调用操作系统API还是两者都有?我知道编译后的程序可以通过反汇编来生成汇编语言代码,汇编语言不就是直接操作机器的语言么?

阅读 31.5k
6 个回答

C语言编译出来的是object文件,里面的内容基本上包括:

  • 跟CPU类型相关的机器代码,这个代码当然在兼容的CPU上都是可以运行的
  • 常量,例如字符串等等
  • 符号表
  • 外部符号表,代码里面引用到的其他文件中的符号,例如printf这种外部标准库提供的函数。这需要等到链接的时候来解决。
  • 输出符号表,代码里面公开的可以被其他文件引用的符号,例如自己写的函数。

这个文件的格式在不同操作系统上是不一样的。所以,虽然其中的机器码可以在CPU上直接执行,但是就算你写的是一个简单的Hello World,也需要用到很多的系统调用。你应该不会想自己去写一个磁盘引导程序,然后使用BIOS中断来初始化显卡进入图形模式,然后往显卡缓冲区里填入数据(当然你的程序里面还需要内置好一个ASCII字库,为了简单起见就不用矢量字库了,假设是点阵的吧),就为了在屏幕上显示一行字吧?当然,这还没有考虑任务调度、内存管理、输入输出、硬件抽象层这些操作系统的基本功能。而且,就算你的程序不用到任何输入输出,只是计算简单的1+1,一段机器码也不是那么简单地扔给CPU就能运行了,至少你得找个人替你“扔”这段代码吧,谁来调用你的main方法呢?这是C Runtime Library干的事,你用C写的代码至少得链接到这个库上。这个库负责创建一个进程,然后调用你的main函数。而这个库在不同操作系统上以及不同的编译器上也是不同的。

此外,假设你只是想写一个静态库,让在不同平台上的软件都能直接引用你写的一个函数,那也还需要跨越文件格式这个障碍,Unix上面用的是ELF格式,Windows用的是PE COFF。

总的来说,你的问题太大了,不好回答,而且就算详细解释了,你也不见得听得明白,我的建议是,你应该先找一些操作系统原理之类的教材学学,打好相关的基础,很多问题自然你就明白了。

之前的回答

这个问题应该在101新手区问。

不过可以简单说一下,操作系统的作用尽可能合理地管理和分配资源(资源包括CPU、内存、硬盘、打印机及其他各种外设),这样用户程序可以只要做他需要做的事情,而不必关注底层细节。

举例来说,你写一个Hello World,不需要告诉电脑从内存中什么位置取数据,在屏幕上哪些像素点要显示什么内容;同时你还可以打开快播看小电影。

编程语言(Programming Language)分为低级语言(Low-level Language)和高级语言(High-level Language)。机器语言(Machine Language)和汇编语言(Assembly Language)属于低级语言,直接用计算机指令编写程序。而C、C++、Java、Python等属于高级语言,用语句(Statement)编写程序,语句是计算机指令的抽象表示。
计算机只能对数字做运算,符号、声音、图像在计算机内部都要用数字表示,指令也不例外,机器语言完全由十六进制数字组成。最早的程序员都是直接用机器语言编程,但是很麻烦,需要查大量的表格来确定每个数字表示什么意思,编写出来的程序很不直观,而且容易出错,于是有了汇编语言,把机器语言中一组一组的数字用助记符(Mnemonic)表示,直接用这些助记符写出汇编程序,然后让汇编器(Assembler)去查表把助记符替换成数字,也就把汇编语言翻译成了机器语言。汇编语言和机器语言的指令是一一对应的,汇编语言有三条指令,机器语言也有三条指令,汇编器就是做一个简单的替换工作
C语言的语句和低级语言的指令之间不是简单的一一对应关系,一条a=b+1;语句要翻译成几条汇编或机器指令,这个过程称为编译(Compile),由编译器(Compiler)来完成。
平台这个词有很多种解释,可以指计算机体系结构(Architecture),也可以指操作系统(Operating System),也可以指开发平台(编译器、链接器等)。不同的计算机体系结构有不同的指令集(Instruction Set),可以识别的机器指令格式是不同的,直接用某种体系结构的汇编或机器指令写出来的程序只能在这种体系结构的计算机上运行,然而各种体系结构的计算机都有各自的C编译器,可以把C程序编译成各种不同体系结构的机器指令,这意味着用C语言写的程序只需稍加修改甚至不用修改就可以在各种不同的计算机上编译运行。各种高级语言都具有C语言的这些优点,所以绝大部分程序是用高级语言编写的,只有和硬件关系密切的少数程序(例如驱动程序)才会用到低级语言。即使在相同的体系结构和操作系统下,用不同的C编译器(或者同一个C编译器的不同版本)编译同一个程序得到的结果也有可能不同,C语言有些语法特性在C标准中并没有明确规定,各编译器有不同的实现,编译出来的指令的行为特性也会不同,应该尽量避免使用不可移植的语法特性。
总结一下编译执行的过程,首先你用文本编辑器写一个C程序,然后保存成一个文件,例如program.c(通常C程序的文件名后缀是.c),这称为源代码(Source Code)或源文件,然后运行编译器对它进行编译,编译的过程并不执行程序,而是把源代码全部翻译成机器指令,再加上一些描述信息,生成一个新的文件,例如a.out,这称为可执行文件,可执行文件可以被操作系统加载运行,计算机执行该文件中由编译器生成的指令。

摘自:《Linux C编程一站式学习》
在线教程

为什么把C语言编译后的代码叫机器码,因为C语言通过编译器->汇编器后生成的代码,CPU是能直接读懂的。

假设你足够了解CPU和它的工作原理,你写好C代码,把他翻译成CPU指令的二进制代码,然后再把这段二进制代码烧录到一块单片机上,这段代码就能运行。

为什么要操作系统,因为需要它来管理硬件和各种程序,特别是内存的管理和程序的进程安全。

你的代码编译成机器码后也是调用系统api,难道你指望他能自己去操纵硬件么?

---

以上这句话针对的是,你在操作系统里写了一个类似hello world的程序。
你当然可以直接面向硬件接口编程,类似操作系统中的驱动程序。

---

为展示伪小白的智商下限,补充伪小白评论:

照你这么说,操作系统是天生就能操作硬件了?
硬件的中断号不然用来干什么的?不就是让程序来操作硬件的吗?
“@scy 你的程序要是操作系统下编译的,你代码里都是虚拟地址,当然是要操作系统接管了。要是我做单片机开发,没操作系统,我编译的代码,我烧到板子上,CPU就是能运行。那是不是可以说,C语言的编译后的机器码,CPU能读懂?”
“硬件的中断号不然用来干什么的?不就是让程序来操作硬件的吗?”
“@scy 没什么好争论,编译后代码都是各种寄存器的存取,不像你说的,编译后的代码还去调用系统API”
@scy 指出来,错在哪?按你那种对编译的理解,当然是错的,不要出来当小丑了, gcc -S -o就是你所谓的编译后的代码啊?
@scy 不说什么了,如果有人看到这些争论,自然会知道谁对谁错。还他妈问别人的知识背景。你是真高手。

---

想说的是,这种伪小白用户,危害更大。
segmentfault这个社区刚来不久,就碰到这种假装高手的伪小白,纯粹的秀智商下限嘛?
和一个智商没有下限的家伙辩论,真的是一件恶心的事情,会严重拉低自己智商,并且浪费宝贵时间。
中国的程序员社区环境已经恶劣至此了么?
麻烦评论前,先把各种基本知识补充完整再来。
当然希望这种人以后还是不要再出现了,这种和强盗没分别,浪费本人一小时时间,就是几百的RMB;不知道哪家公司会用这种家伙,或许他根本就没进过公司。

说不几句话就开始用国骂,这种素质拉低了整个中国程序员的水平。

如果你想对别人的答案指手画脚前,请先确认你对此有足够的了解;如果你自认了解,请另开新帖展示你自己的言论。谢谢!

我觉得LZ是把机器语言和OS系统弄混了, 其实两个没多大关系,至少我感觉是这样, 三楼的回答已经很准确了

其实我的理解就是C C编译之后 操作系统 机器,其实就类似厨师和菜还有锅碗瓢盆的关系
你种菜,就是写C 菜成熟了 C写好了 你既要洗菜切菜,相当一就是编译
编译完成之后菜不是自己进到锅里面来炒的,还需要一个厨师,也就是操作系统,锅碗瓢盆就像是机器。
虽然说菜洗干净了,只要放进锅了就能做成能吃的菜,但是还是需要一个中间介质。

当然这样通俗的举例不知道对不对,实在没那个能力像楼上这些大神那样用专业术语解释清楚,呵呵

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏