中山野鬼 linux 下 C 編程和make的方法 (3、工程文檔的組織)

一些新手搞不清楚工程,和源代碼,C文件,頭文件的區別。這裏特意爲新手說明一下:
    不管你是否寫過程序。你從用過軟件。你會發現不多一個軟件就一個文件。你能夠在window下看一下某個具體軟件的位置,並在這個位置打開文件夾,會發現 有不少文件。從設計軟件或程序的開發角度也是同樣的,一個程序不少狀況下,除非足夠簡單,你只用一個C文件便可。例如:
1 int main(int argc,char *argv[]){
2     return argc;
3 }

這個文件你甚至都不須要#include <stdio.h>。函數

但若是你想打印個信息在屏幕上,如」hello world",則你須要調用別人幫你作好的庫函數,例如printf,而此時,雖然即使你仍是一個C文件能夠生成程序,可是你已經用到另外兩個文件。一個 是頭文件 stdio.h。一個是存放printf設計實現的庫函數。他們在哪這不是目前關係的,但至少你的設計中包含了不止一個東西。此時,你沒法用一個源代碼 (如你上面本身寫的那個C文件)來描述你操做的範圍。此時這個總體就是工程。工程是個抽象的名詞,和系統同樣,很抽象。學習


    須要說明,文檔組織形式,沒有什麼國際標準。每每本身習慣的,就是最好的(基於這種習慣能保證你的工做效率)。這裏先介紹一下我喜歡的工程組織形式,一個目錄下至少存在這樣幾個子目錄。
 src  ; inc ; doc ;obj;bin
    必要時,還須要有asm,output,input,lib
    src,很簡單,對應裏面存在的是c文件。和inc分離,是由於可能存在asm,或非C代碼的程序文本,非C代碼的程序文本獨立使用一個目錄組織,但C的 編譯器使用外部其餘語言編寫的模塊時,仍是須要接口說明,這些說明每每也是放在.h裏。所以分割爲src和inc,這是個好注意,不是我發明的。
    inc,裏面存放的是.h文件,或其餘預編譯時有效的文件
    doc,裏面存放的是說明文檔。文檔就是文檔。和工程的模塊編譯執行,毛關係沒有。你大能夠在轉移工程內容時,脫離掉doc。分類出一個doc目錄,方便 實際查詢資料和代碼管理。例如版本控制程序,多半是用遞增方式維護的,雖然有不一樣版本,可是是將兩個版本的差別進行保存。而文檔,一般是流水方式,依次記 錄過程當中對代碼的調整歷史,這類資料獨立出一個目錄是有必要的。
    obj,裏面存放的是.o文件,這個不用說了,
      
1 $rm obj/*
       是多麼的方便,若是你但願rebuild all的時候。
    bin,裏面存放的是靜態庫,動態庫,執行文件,準確說是鏈接器的輸出文件存放的地方,一般爲了方便尋找到你折騰半天所想要的最終生成的東西。例如你編譯 了半天,但願結果的文件能遠程加載到SD卡里,open一個bin的專門目錄,比在一堆文件中求索哪一個纔是你須要的要方便不少。固然這裏的所謂庫,都是由 當前這個總目錄下生成的庫文件,而不是別人給你的,別人給你的,一般建議,你增長個lib目錄,或增長環境配置,這另談。這裏須要補充糾正一個觀點,靜態 庫,並非實際的鏈接工做。其實是個歸檔的工做。將一堆obj歸檔到一個文件中。一般使用ar命令。但廣義的說鏈接,這種歸檔整理的工做也算是一個,這 是野鬼版的胡攪蠻纏。
    asm,裏面都是彙編源碼文件,實際是不少須要彙編實現的函數保存的文件。大多數人不須要。但個人工做常常用到手寫彙編沒辦法,單片機,ARM,DSP都折騰過工程級的開發,因此沒辦法,習慣了src,asm分離。
    input,output,一個裏面存放的是程序執行時的輸入資料,一個存放的是程序執行時的輸出資料。這個在不少工程裏,並不存在,只是個人工做常常遇 到大批量的輸入數據資料,來驗證代碼的正確性和有效性。此時將輸入輸出歸到子目錄裏,只是方便處理而已。同時,對於程序設計而言,也比較好規範出一個習 慣,對輸入輸出的接口有個比較規範的組織形式。否則,代碼執行,老是在當前目標下查找資料,或者在環境變量path下查找,畢竟不能解決全部問題。

    針對learn_make,咱們開始作以下調整。 ui

1 $mkdir src
2 $mkdir inc
3 $mkdir obj
4 $mkdir bin


至少先這4個。能簡單的別複雜了。
1 $mv learn_make.c src/
2 $mv learn_make.h inc/
3 $rm *
這裏會提示說,有些是目錄,沒法刪除。這是好事情,咱們須要將learn_make目錄下的文件刪除乾淨,目錄不動。

    將來每一個文件都會有對應當前目錄下的具體存放位置,經過我上面的理由作出的約束規範。後期這有個好處,C語言和其餘語言同樣,但願代碼可以複用,儘量的 讓模塊能夠在不一樣工程裏均有效實現。而C語言的目標是一次編寫,處處編譯(別迷信「一次編譯,處處運行」這種廣告用語,和街頭的「老軍醫」沒什麼區別),spa

     所以C語言但願C的源代碼,按照C的文本文件爲最小單元,可以在不一樣工程裏複用,所以經過目錄,子目錄的方式,能夠有效提升操做者後期代碼文本文件的組織能力(其實就是源代碼,但但願能增強新手對源代碼的認知,它只是個按照語言規則書寫的文本文件)。設計

       我說了,我不期望個人資料能夠提升新學者的智商;個人目的是,提升新學者管理組織工程的能力,使得當工程規模變大時,新學者能夠相對其餘人更好的駕馭工程。在你智商不明顯高出競爭者時,你提升復瑣事務的控制能力,是一個擊敗對手的比較可行的辦法。版本控制

    如今咱們在learn_make下,運行gcc,看看有多少累。先說下,爲何在learn_make目錄下執行gcc。由於除了這,你在那執行都麻煩。 你要在src裏,當你的輸出和引用頭文件,都須要退出本目錄,到上一級目錄後,再進入對應子目錄,既然一堆孩子都爭吵但願gcc在本身目錄下執行,那乾脆 一碗水端平,老爸本身佔便宜得了。
1 $gcc src/learn_make.c
 哈,這裏確定有個錯誤,由於learn_make.h咱們移動到inc目錄下了,說錯誤,是但願引出一個gcc的參數,-Idirectory。 同時也建議新手,就是錯誤,也要執行一次,當屏幕上,先存在錯誤提示,後出現正確提示時,這種差別,會刺激你的大腦,快速的記住知識。想迅速快速入門,那麼最好你就不停的跳進個人坑裏,看看坑是什麼模樣的,之後纔有認識真正的陷阱的能力。

    須要補充說明的是,gcc對參數是大小寫區分的,-c和-C是不同的,-i和-Idirectory也是不同的。這裏我特地使用了gcc參考資料的描 述,而沒有使用  -I的描述是但願新學者注意,目錄名和I是沒有空格的。他們組成了一個新單詞。此處小寫的directory是抽象的描述你但願表達的目錄。若是你存在多 個目錄裏面都有頭文件須要引用,假設當前目錄下,存在inc1, inc2兩個放頭文件的子目錄,那麼你須要  -Iinc1 -Iinc2。 code

1 $gcc -Iinc src/learn_make.c
2 $ls
  注意下,如今顯示的是/learn_make目錄。
    一切又正常了。但沒有達到咱們的要求。由於當前目錄下,有了個a.out。咱們但願a.out存放在bin下。你徹底能夠繼續這樣執行。
  
1 $mv a.out bin/
2 $bin/a.out //此處是調用bin下的a.out執行。


但確實夠「弱智」的。能夠考慮修改一下特定輸出目標。
   
1 $gcc -Iinc src/learn_make.c -o bin/learn_make
2 $ls bin //查看bin目錄
3 $bin/learn_make
  很爽吧,當前目錄下,很乾淨,並且各自文件都納入到本身的位置。
    可是生成的.o文件,是值得保留的。這樣能夠提升效率。所以咱們須要繼續調整方法
1 $gcc -Iinc -c src/learn_make.c -o obj/learn_make.o
2 $ls obj
記得雖然你指定了目標文件,learn_make.o。可是若是沒有-c, gcc會生成一個learn_make.o的可執行文件(這包含了連接的動做),而不是你想要的obj。

    有些所謂高手說我這是廢話,資料上不是說的清清楚楚嗎?可是就是這麼清楚,這樣筆誤仍然會致使一個.o的出現,後果很嚴重。這是個很大的「陷阱」因此我要反覆弱智的說一下。    接口

1 $gcc obj/learn_make.o -o bin/learn_make



     這裏不須要 -Iinc了。由於須要inc目錄是由於你的c文件須要#include "learn_make.h",在預處理時,須要在合適的位置(目錄裏)找到對應頭文件,此時是對obj的鏈接工做,和頭文件已經沒有任何關係了。
1 $ls bin
2 $bin/learn_make
若是你如今尚未慾望學習make的話,不妨修改一下,learn_make.c的以下語句
   
01 #include <stdio.h>
02 #include "learn_make.h"
03 #include "learn_make1.h"
04 #include "learn_make2.h"
05 #include "learn_make3.h"
06 int main(int argc,char *argv[])
07     printf("%s\n%s\n%s\n%s\n",\
08 TEST_GCC_CMD,TEST_GCC_CMD1,TEST_GCC_CMD2,TEST_GCC_CMD3);
09       
10     return 0;
11 }
12 //以上是src/learn_make.c的文件
13 //如下是learn_make/inc1/learn_make1.h的內容
14 #ifndef _LEARN_MAKE_1_H_
15 #define _LEARN_MAKE_1_H_
16 #define TEST_GCC_CMD1 "test gcc cmd1"
17 #endif// _LEARN_MAKE_1_H_
18 //一樣,你在learn_make/inc2 ;learn_make/inc3裏也建立相似的頭文件,<code>方便主函數調用我不一一展開</code>


  如上,相似learn_make.h的方式,編輯learn_make1.h, learn_make2.h, learn_make3.h, 分別存放到learn_make/下的新目錄inc1,inc2,inc3下。
    你爲了保障你的編譯能夠經過,你得這寫。
    $gcc -Iinc -Iinc1 -Iinc2 -Iinc3 -c src/learn_make.c -o obj/learn_make.o

    「很長,很暴力」。新手會說「你弱智啊」,爲何要搞這麼多inc 目錄。恩,這個例子很弱智,但表現了一些大型工程中會遇到的典型問題。事務

     大型工程,首先是分組實現的,不一樣組的代碼會在不一樣目錄下,這樣方便管理。同時你可能引用第三方的庫,須要依賴第三方的頭文件。但這些頭文件你都不方便放到glibc庫的頭文件或系統庫的頭文件目錄下,所以你不能用開發

1 #include <XXX.h>

的方式實現。你也不可能由於目錄的不一樣用絕對路徑實現例如

1 #include "/usr/src/learn_make/inc/learn_make.h"
  這是很不開源的作法。這樣意味着,使用你代碼的人必須重現你的所有工做環境。你可能會說「關我屁事!」,惋惜這我的說不定就是你本身呢。

     因此C語言設計,千萬不要將文件的路徑問題,在代碼中固化下來。與人與己都是有利的。相對路徑會好點,例如,你也潛規則你們,默認C文件在src下,頭文件在inc下,並且src和inc在同一個目錄下,則能夠

1 #include "../inc/learn_make.h"
   但這個問題治標不治本,同時不方便後期的源代碼管理。因此仍是老實的用-Idirectory吧。廢話這麼多,是但願把你說暈了,好跳到個人坑裏,當我把你說暈,必定要用-I時,實際上,我已經挖好了make的坑,並且你已經打算主動跳進去了。
相關文章
相關標籤/搜索