源程序到運行還差多遠

本文所介紹的是like-unix 系統下的相關工做原理,適合於初學者和app開發者。linux

       做爲通常的碼農來講,大部分時間咱們都在爲各類邏輯而煞費苦心,然而你是否利用太短暫的瞬間想過(特別是在集成環境下編程的程序猿),你編寫的程序到運行還須要哪些步驟呢?有些碼農會說,我想它幹啥,編譯器都會爲我所有處理好。是,如今的編譯器確實很強大。可是你編譯過程當中是否遇到過錯誤呢?——特別是大型程序。好比,你是否遇到過這個錯誤「undefined reference to 【function】(function表明某個函數名稱)」呢?當你遇到了錯誤知道檢測程序的哪一個部分嗎?。這也是我寫此文的目的,不是爲了研究如何去編譯一個程序,而是當咱們遇到編譯錯誤時,可以快速找到問題所在,及時處理問題。c++

       首先咱們明確幾個術語:編程

       源文件:就是程序猿使用各類編輯器敲出來or拷貝過來的程序猿可以看懂的代碼,例如c語言的.c,.h文件,c++的.cpp和.h文件。app

       對象文件:源文件經過編譯器編譯後產生的機器碼文件,如.o文件。編輯器

       可執行文件:屬於對象文件的一種,最後能夠被操做系統加載running的文件。函數

       碼農編寫完成的程序到運行大體要進行如下幾個步驟:spa

如本例中所示,hello.c就是咱們用c語言編寫的程序,稱爲「源文件」。操作系統

       在linux中咱們使用「gcc -o hello hello.c」命令,首先gcc會進行一個「預處理」,包括「include」頭文件包含處理、宏定義替換處理和條件編譯處理,就是源文件中以「#」開頭的代碼,都屬於預處理器要處理的。unix

       當預處理完成後,會生成一個helo.i文件,這個文件也是個text文件——程序猿能看懂的文件。接着編譯就上場了,編譯器將c語言編寫的源程序轉換爲彙編語言的hello.s文件,此時編譯器就能夠回家休息了。對象

       hello.s文件也是個text文件——通常程序猿可看不懂,要懂彙編語言的才能理解其意思。那麼此時的hello.s文件還不是computer能懂的語言,接着彙編器就該出場了。它將hello.s文件編譯成對象文件hello.o文件——二進制文件,此時的彙編器也就完成本身的使命了。

       既然hello.o已是computer能看懂——程序猿看不懂——的文件了,是否是就能夠running了?wait。雖然此時的hello.o文件已是對象文件,但它還不能running 。why?想一想即經典又簡單的「hello world」程序(彷佛每種語言講解書的第一個程序都是它)中是否調用了庫中的輸出函數(好比c語言中的printf)。咱們的程序中沒有對這個函數實現,compter運行到調用輸出函數那個指令,怎麼會知道它的運行地址呢?這就是鏈接器的做用了。

      鏈接器的工做就是將一個.o文件中一個函數(不在這個.o文件中定義)的引用和這個函數的定義肯定清楚。簡單來說就是將全部的.o文件和引用到的靜態庫中的模塊全都拷貝到一個新的對象文件中,這個新的對象文件中所引用的全部函數和變量都能在其中找到它們的實現。這個文件就是咱們所說的可執行文件。到此該文件就能夠被computer加載running了。

      可執行文件的組織方式和咱們的源代碼會有很大區別,它能夠被compter方便的加載到內存中。通常會分爲幾個section,好比代碼 section和數據 section。每一個代碼和數據的virtual  memory 地址都會被分配好(局部變量在running-time才分配)。

      上面hello.c只是一個很簡單的例子。在平時的工做中,爲了提升代碼的可重用性,一個程序通常都會包含不少實現文件和頭文件。每一個實現文件編譯完成後都會對應一個.o文件(頭文件在預處理過程會被拷貝到引用該頭文件的實現文件中)。而「undefined reference to 【function】(function表明某個函數名稱)也容易出現,特別是在引用了多個庫文件的時候。這就是鏈接器報出的錯誤,緣由是鏈接器找不到這個函數的實現對象文件(即實現文件被編譯後的.o文件)。

相關文章
相關標籤/搜索