課堂裏學不到的C與C++那些事(一)

首先,聲明一下這是一個系列的文章。至於整個系列有多少篇,筆者也不知道,不知道有多少篇,也不知道多久會更新一篇。反正只有一個原則,寫出來的文章能見得人才會公佈出來。另外,我不是叫你逃課,而是覺得聽課只是一般學生做的,聽課的時候把該聽的聽了,不該聽的聽過就算了,課堂上的東西只不過是大千編程界裏細沙一粒也稱不上,真正牛的人從不滿足那一小點知識,有些事太認真你就輸了,世界很大,不要侷限自己的視野,那樣會很累。

首先:整個系統環境都是基於linux平臺上的,如果有興趣你可以參考這裏去學習linux:如何成爲一個真正在路上的Linuxer   ,也推廣下團隊剛搭建出的LinuxCoder 社區

編譯器用的gcc、g++,沒有請先自行安裝。

 

編譯第一個可執行文件

第一篇文章,照例寫個最經典的hellow word 程序吧。(代碼1)

// code by lfly

// 2014-11-22

#include <iostream>

using namespace std ;

int main( int argc, char **argv)

{

    cout << “Hellow World!” << endl ;

    return 0 ;

}

很簡單的一段代碼,最後返回0表示成功退出,返回其它值代表的是某種錯誤(看具體值)。保存爲hellow.cpp文件然後編譯運行下:

lfly@programfish:~/project/c> ls hellow.cpp

lfly@programfish:~/project/c> g++ hellow.cpp -o hellow.o

lfly@programfish:~/project/c> ls hellow.cpp  hellow.o

lfly@programfish:~/project/c>

-o是指定結果文件名,這裏編譯成目標文件hellow.o

如果不用-o指定文件名,默認是編譯成a.out文件的。

.out文件是編譯鏈接成的可執行文件,而.o文件一般是編譯出來的一個目標文件,還沒有鏈接的。

但是Linux下是不以後綴名來區別是否是可執行文件,區別的標準只有一個:該文件在對應的用戶下有沒有執行權限(x權限)。 好了,運行一下:

lfly@programfish:~/project/c> ./hellow.o

Hellow World!

正是我想要的結果: Hellow World!

 

main 函數參數:

main 函數裏有兩個參數,第一個是int類型argc,表示傳入main函數的參數個數。第二個是一個二維字符指針argv,保存了各個傳入的參數。這裏注意,argv[0]是保存了執行這個可執行文件時的路徑,後面argv[1]到argv[argc-1]纔是保存了用戶傳入的參數(如果有的話)。 這裏改動一下程序:(代碼2)

// code by lfly

// 2014-11-22

#include <iostream>

using namespace std ;

int main( int argc, char **argv)

{

 cout <<  “argc is: ” << argc << endl ;

for ( int i=0; i<argc; ++i )

{

 cout << argv[i] << endl ;

 }

return 0 ;

}

改成這個樣子,輸出數量argc和argv裏的各個字符串。依然是上面的編譯命令然後:

g++ hellow.cpp -o hellow.o

然後隨便加兩個參數hellow、world運行一下:

lfly@programfish:~/project/c> ./hellow.o  hellow  world

argc is: 3 .

/hellow.o

hellow

world

可以看到參數數量爲3,因爲默認的第一個參數是執行的路徑(這裏爲./hellow.o)其餘兩個爲傳入的hellow 及world

注意:main函數可以寫成不帶參數或(void)的。而main函數最初最初是不帶參數的。想了解main函數身世請看這裏:你所未必瞭解的main()函數的事情 http://www.nowamagic.net/librarys/veda/detail/96

 

窺探編譯與鏈接過程

g++編譯鏈接文件過程:

預處理 —> 編譯(彙編文件) —> 彙編(機器碼) —> 鏈接(可執行程序)

1預處理過程

生成.i文件,這一下由預處理器cpp程序執行。

cpp是一個可執行程序,一般路徑爲/usr/bin/cpp(可能是一個鏈接),你可以用find命令去搜索一下具體路徑。預處理器會讀入源代碼然後查找出預處理指令(宏定義、文件包含、條件編譯),這些指令以#開頭。

  •   宏定義

宏定義是指#define指令,預處理過程會把這些宏展開,例如        #define  DF  10

預處理會把程序代碼裏出現的DF獨立組合替換成10,這是簡單的宏定義,至於帶參數的宏定義在這裏不作討論。

  •   文件包含

指#include 指令,預處理器會把包含到的頭文件的內容替換到這個#include 指令。

  •   條件編譯

#ifdef(#ifndef)與#endif指令,這些指令很大作用是使編譯出來的目標文件不會過大,你想想上面的#include指令會把一個頭文件的內容替換到cpp文件裏,假如你大意重複包含了文件(文件A包含文件B,在文件C裏包含了A,然後也用到B所以包含了B,那麼C就包含了兩次B),這種情況在複雜的工程很難避免。所以用條件編譯可以優化你的程序,當然它還有其它別的重要的作用,這裏不討論。

除了處理預編譯指令,預處理器還會刪掉你的註釋(機器不看你的註釋,估計也看不懂)。然後還有保留#pragma指令。

  好了,現在來看看我們的hellow world預處理後會是什麼樣子的。簡單起見,使用上面代碼1作爲源代碼,使用g++ -E 預編譯(當然你可以直接使用cpp命令)

g++ -E hellow.cpp -o hellow.i

然後來看一下預編譯得到hellow.i這個文件的內容:

圖片1

簡單幾行代碼預編譯後得到的文件足足有17563行。而我開頭的兩行註釋確實沒有了。

2編譯成彙編文件    

這個過程是把預編譯後的代碼編譯成彙編代碼,由編譯器egcs執行。下面來編譯一下我們得到的hellow.i文件:

g++ -S hellow.i -o hellow.s

然後查看一下hellow.s文件:

圖片2

都是彙編代碼,學彙編的記得保重身體啊…..

3彙編過程  

這一步就可以得到.o目標文件了。過程由彙編器as執行,把上面得到的hellow.s文件裏的彙編指令逐條翻譯成機器碼。

g++ -C hellow.s -o hellow.o

4鏈接過程  

由鏈接器ld完成,把多個.o的機器碼鏈接成.out這樣的可執行文件(當然後綴名不是重點)。注意,我這裏的只有一個.cpp文件,所以就只有一個.o文件,不需要鏈接,這只是示例,但是正常工程下肯定不止這一個文件,那時候就要鏈接成可執行文件纔可以運行了。

以上是Linux平臺裏的示例,你可以在windows下使用g++做上面同樣的步驟。但作爲程序猿,建議你使用linux做開發,不要問爲什麼,可以找我博客裏關於linux的文章看看。   第一篇就討論到這裏吧,下次更新再討論其它問題。



 歡迎訪問本人網站:http://www.programfish.com 

LinuxCoder社區: http://linuxcoder.org

注意:轉載請註明 “作者:廣州Linux愛好者+雲計算 刁金明”



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章