C語言編程機制

C語言編程機制

2 人贊了文章

1972年,貝爾實驗室的丹尼斯·里奇和肯·湯普遜在開發UNIX操作系統時設計了C語言。從那時開始,C語言已經從其位於貝爾室驗室的發源地傳播到世界各地,它已經成為全球程序員的公共語言。

儘管在過去的20多年裡,許多人都從C語言轉而使用其他編程語言(如C++、Java等),但因為C語言具有彙編語言才具有的微調控能力、與UNIX系統的緊密聯繫等優點,使得其在40多年後的今天依然經久不衰。如果要在我的工作技術棧之外掌握一門新的語言,毫無疑問C語言是我的首選。本文將簡單介紹一下C語言的編程機制,為以後的深入學習打下堅實的基礎。

用C語言編寫程序時,編寫的內容被存儲在文本文件中,該文件被稱為源代碼文件。大部分C系統,都要求文件名以.c結尾,下面是hello.c文件的源代碼:

#include <stdio.h>int main(void){ printf("hello world!"); return 0;}

C編程的基本策略是,用程序把源代碼文件轉換為可執行文件。典型的C實現通過編譯鏈接兩個步驟來完成這一過程。編譯器把源代碼轉換成中間代碼,鏈接器把中間代碼和其他代碼合併,生成可執行文件。

中間文件有多種形式。我們在這裡描述的是最普遍的一種形式,即把源代碼轉換為機器語言代碼,並把結果放在目標代碼文件中。雖然目標文件中包含有機器語言代碼,但是並不能直接運行該文件。因為目標文件中 存儲的是編譯器翻譯的源代碼,這還不是一個完整的程序。那麼目標文件相比完整的程序缺少了什麼呢? 1. 目標代碼文件缺失啟動代碼。啟動代碼充當著程序和操作系統之間的介面。 2. 目標代碼還缺少庫函數。幾乎所有的C程序都要使用C標準庫中的函數。

鏈接器的作用是,把你編寫的目標代碼、系統的標準啟動代碼和庫代碼這三部分合併成一個文件,即可執行文件。對於庫代碼,鏈接器只會把程序中要用到的庫函數代碼提取出來。

閱讀本文至此,你應該對C語言的編程機制有了一個整體的認識。現在我們通過上文提到的入門程序hello.c來觀察一下這個過程。 首先我們使用gcc編譯器編譯hello.c源程序:

gcc hello.c

因為沒有指定文件名,默認會生成一個名為a.out的可執行文件,我們可以在命令行執行它:

./a.out// 輸出hello world

程序的輸出結果我們已經看到了,但是或許你有很多的疑問。比如a.out這個文件的後綴名有些新穎,裡面的內容又是什麼呢?如果你是個急性子,說不定已經在命令行輸入vim a.out去揭開它的廬山真面目了,然而你看到是類似這樣的東西:

當你看到這樣極其不利於閱讀的內容後(由彙編器和鏈接器生成的,內容是二進位,而非可讀的文本形式),可能一時之間不知道如何去理解它。不要急,讓我們先來看看a.out這個文件是什麼:

a.out是舊版類Unix系統中用於執行檔、目的碼和後來系統中的函數庫的一種文件格式,這個名稱的意思是彙編器輸出。 儘管目前大多數類Unix系統都已改用ELF格式,不再採用a.out格式,但編譯器和鏈接器依然會在用戶未指定文件名時,將輸出文件取名為「a.out」。

現在我們明白了a.out這個文件的來由了,但是在介紹它的時候說過大多數類Unix系統都已改用ELF格式。因此,我們要了解a.out文件這個問題的本質就變成了去了解ELF格式的文件了。

ELF是Executable and Linkable Format的縮寫形式即可執行和可鏈接格式,常被稱為ELF格式,最初由UNIX系統實驗室做為應用程序二進位介面的一部分而制定和發布。 ELF規範把ELF文件寬泛地稱為目標文件,這與上文提到的目標文件不一樣。一般地,我們把經編譯但沒有鏈接的文件(比如Unix/Linux系統上的.o文件)稱為目標文件,而ELF文件 僅指鏈接好的可執行文件;但在ELF規範中,所有符合ELF格式規範的都稱為ELF文件,也稱為目標文件,這兩個名字是相同的,而經過編譯但沒有鏈接的文件則稱為」可重定位文件「或」待重定位文件「。 目標文件/ELF文件主要分為以下三種類型: 可重定位文件,也就是本文最開始介紹的目標文件,由源文件編譯而成,但還沒有鏈接成可執行文件。 共享目標文件,即動態鏈接庫文件。 * 可執行文件,經過鏈接的、可以執行的程序文件。

從鏈接的角度和運行的角度,可以分別把目標文件的組成部分做以下的劃分:

ELF文件頭(ELF header)位於文件的最開始處,包含有整個文件的結構信息。節(section)是專用於連接過程而言的,在每個」節「中包含有指令數據、符號數據、重定位數據等等。程序頭表(programheader table)在運行過程中是必須的,在 鏈接過程中是可選的,因為它的作用是告訴系統如何創建進程的鏡像。節頭表包含有文件中所有」節「的信息。在鏈接視圖中,」節頭表「是必須存在的。

由於篇幅所限,關於ELF文件相關的內容只能介紹這麼多,現在讓我們回歸到要解的問題上去。剛才我們說過查看a.out文件內容就是查看ELF文件內容,而查看ELF文件內容我們可以使用readelf相關命令。現在我們通過以下命令查看a.out文件內容:

readelf -a a.out

除了使用readel命令查看ELF文件,我們還可以使用objdump命令.那麼readelf命令和objdump命令的區別是什麼呢,參考quora上一位網友的回復:

The readelf program can dump all the information in an ELF file. ELF is the object file format used on pretty much all modern operating systems, except for Windows and Mac OS. The objdump program can dump generic information from a file in (almost) any object file format, including ELF but also including COFF/PE (used on Windows), Mach-O (used on Mac OS), S-records, IHEX, etc. It can dump some file-format-specific information, but not all. So if you know you have an ELF file, use readelf. If you don』t know, use objdump.

一部分人看到這些命令後也許就有些不耐煩了,我只是想打開文件看看裡面長啥樣,你給我介紹一堆其他命令,就沒有簡單粗暴一點的解決方案嗎?這個真的可以有,我們可以用ht editor直接打開這些可執行文件。在mac下我們可以通過下面命令安裝ht editor:

brew install ht

在命令行中輸入ht進入ht editor的編輯界面,通過f3打開文件,顯示結果如下:

說了半天都是關於a.out文件,上文我們提到過編譯器最開始是生成目標代碼的,其拓展名是.o。然而我們在編譯代碼的過程中卻沒看到這個後綴為.o的文件,這是因為一旦鏈接器生成了完整的可執行程序,就會將其刪除。我們可以通過以下命令生成目標代碼:

gcc -c hello.c

這時你在當前目錄就會生成hello.o文件,因為目標文件也是ELF文件的一種,趕快用上面介紹的幾種方法打開看看吧。


推薦閱讀:

TAG:編程語言 | 編程 | C編程語言 |