2010-04-03 88 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系統調用以實際顯示字符。