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文件頭(ELF header)位於文件的最開始處,包含有整個文件的結構信息。節(section)是專用於連接過程而言的,在每個」節「中包含有指令數據、符號數據、重定位數據等等。程序頭表(programheader table)在運行過程中是必須的,在 鏈接過程中是可選的,因為它的作用是告訴系統如何創建進程的鏡像。節頭表包含有文件中所有」節「的信息。在鏈接視圖中,」節頭表「是必須存在的。
由於篇幅所限,關於ELF文件相關的內容只能介紹這麼多,現在讓我們回歸到要解的問題上去。剛才我們說過查看a.out
文件內容就是查看ELF文件內容,而查看ELF文件內容我們可以使用readelf
相關命令。現在我們通過以下命令查看a.out
文件內容:
readelf -a a.out
除了使用readel命令查看ELF文件,我們還可以使用objdump
命令.那麼readelf命令和objdump命令的區別是什麼呢,參考quora上一位網友的回復:
一部分人看到這些命令後也許就有些不耐煩了,我只是想打開文件看看裡面長啥樣,你給我介紹一堆其他命令,就沒有簡單粗暴一點的解決方案嗎?這個真的可以有,我們可以用ht editor
直接打開這些可執行文件。在mac下我們可以通過下面命令安裝ht editor
:
brew install ht
在命令行中輸入ht進入ht editor的編輯界面,通過f3打開文件,顯示結果如下:
說了半天都是關於a.out
文件,上文我們提到過編譯器最開始是生成目標代碼的,其拓展名是.o
。然而我們在編譯代碼的過程中卻沒看到這個後綴為.o
的文件,這是因為一旦鏈接器生成了完整的可執行程序,就會將其刪除。我們可以通過以下命令生成目標代碼:
gcc -c hello.c
這時你在當前目錄就會生成hello.o
文件,因為目標文件也是ELF文件的一種,趕快用上面介紹的幾種方法打開看看吧。
推薦閱讀: