2010-04-03 91 views
6

我想完全理解在某些语言中将代码编写成由OS执行的过程。在我的情况下,语言将是C,操作系统将是Windows。到目前为止,我阅读了许多不同的文章,但我不确定,我是否理解这个过程的权利,并且我想问你,你是否知道一些我无法找到的主题的优秀文章。一些通用的C语言问题

所以,我想我会知道C(基本上其他语言):

C编译器本身只处理的数据类型,基本的数学运算,指针运算,并与职能的工作。通过与函数一起工作,我的意思是如何将参数传递给函数,以及如何从函数中获取输出。在编译期间,函数调用被替换为将参数传递给堆栈,并且如果函数不是内联的,它的调用被链接器的某个符号替换。链接器比找到函数定义,并替换符号跳转到该功能(当然,跳回程序)。

如果上面的内容一般是正确的,我知道它的正确位置,那么最终的.exe文件实际上是链接器保存的功能? main()函数之后?什么创建.exe头文件?编译器还是链接器?

现在,C语言的其他功能,如今被称为C标准库的功能和声明被其他程序员编写来扩展和简化C语言的使用。但是,像printf()这样的函数是用不同的语言或汇编语言编写的(或可能是?)。还有我的下一个问题,可以是,例如printf()函数是用纯C编写的,而不使用汇编程序?

我知道这是一个相当大的问题,但我只是想知道,我是对的还是不对。相信我,我在网上阅读了很多文章,而且我不会问你,如果我能在一篇文章中找到这些信息。尽管我必须一块一块收集信息,所以我不确定我是否正确。谢谢。

+1

我建议你先学会编程。任何好的编程手册都会告诉你链接和编译的一般工作流程。 – 2010-04-03 18:03:46

回答

6

我认为你会接触到一些与C程序员不太相关的信息,这可能会让你感到困惑 - 使用像这样的高级语言的目标的一部分就是不必首先思考这个过程如何工作。然而,随着时间的推移,了解这一过程非常重要。我认为你一般都有正确的理解。

C编译器仅采用C代码并生成包含机器语言的对象文件。大部分的目标文件都是由函数的内容来完成的。例如,C语言中的一个简单的函数调用将以编译后的形式表示为低级操作符,以将事物推入堆栈,更改指令指针等。

C库和您要使用的任何其他库已经以这种编译形式提供。

链接器是将所有相关目标文件合并,解析所有依赖项(例如,调用标准库中的函数的一个目标文件),然后创建可执行文件。

至于语言库的编写方式:把每个函数想象成一个黑盒子。只要黑盒子有一个标准接口(C调用约定,即以某种方式接受参数,以某种方式返回值等等),它如何在内部编写并不重要。最典型的情况是,这些功能可以用C语言编写或直接编译。当它们成为一个目标文件(或作为一个编译过的库)时,它们最初的创建方式并不重要,重要的是它们现在处于编译好的机器形式。

可执行文件的格式取决于操作系统,但windows中可执行文件的大部分内容与目标文件非常相似。想象一下,如果有人将所有的目标文件合并在一起,然后添加一些胶水。 glue会加载相关的东西,然后调用main()。例如,当我还是一个孩子的时候,人们在“改变胶水”的基础上,在main()之前添加了另一个函数,用它们的名字显示一个启动画面。

需要注意的一点是,不管您使用哪种语言,最终都必须使用操作系统服务。例如,要在屏幕上显示内容,管理进程等。大多数操作系统都有一个也可以类似方式调用的API,但其内容不包含在EXE中。例如,当你运行你的浏览器时,它是一个可执行文件,但在某个时候有一个调用Windows API来创建一个窗口或加载一个字体。如果这是你的EXE的一部分,你的EXE将是巨大的。所以即使在你的可执行文件中也有“缺少的参考文献”。通常,这些在加载时或运行时根据操作系统而定。

+0

非常感谢。所以,可以肯定的是,当我在主函数中调用函数时,链接器会添加跳转到该函数并将该函数追加到exe文件的末尾,或者是位于exe文件不同部分的函数,而不是装入器实际上将它们连接在一起 – 2010-04-03 18:40:12

+0

在8051汇编程序中,我只是将例程添加到程序的末尾,并通过输入到它们的跳转和ret入口并返回。但是使用跳转编译器需要知道例程中第一条指令的地址。但是,当Windows将程序加载到RAM中时,编译器无法知道给定例程的第一条指令的地址是什么。 – 2010-04-03 18:46:40

+0

我不知道这是如何在Windows目前的作品,但装载机通常做很多,可以重新定位的东西。例如,在MS DOS的旧版本中,过去对可执行文件的大小有限制,所以如果你有很多额外的代码,在运行时需要特殊的技巧来加载和删除代码。 – Uri 2010-04-03 19:04:54

0

编译器负责将用C编写的所有函数转换为汇编程序,并将其保存在目标文件(例如DLL或EXE)中。因此,如果您编写一个具有主函数和其他几个函数的.c文件,编译器会将所有这些文件转换为汇编,并将它们一起保存在EXE文件中。然后,当你运行该文件时,加载器(它是OS的一部分)知道首先开始运行主函数。否则,主函数就像编译器的其他函数一样。

链接器负责解析一个对象文件中的函数和变量与其他文件中的引用之间的任何引用。例如,如果您调用printf(),由于您自己没有定义函数printf(),链接器负责确保对printf()的调用转到正确定义了printf()的系统库。这是在编译时完成的。我们可以在操作系统中调用一个系统调用,它知道如何将字符实际发送到标准输出(如窗口终端)。当您在程序中调用printf()时,在编译时,链接器负责将您的调用链接到标准C库中的printf()函数。在运行时传递函数时,printf()会正确格式化参数,然后调用相应的OS系统调用以实际显示字符。