Ubuntu深入学习
Ubuntu深入學習
- 一、可執行程序是如何被組裝的?
- 二、用gcc生成靜態庫和動態庫
- 1.編輯生成例子程序
- 2.將hello.c編譯成.o文件
- 3.由.o 文件創建靜態庫文件
- 4.在程序中使用靜態庫
- 5.由.o 文件創建動態庫文件
- 6.在程序中使用動態庫
- 三、靜態庫應用
- 1.建立文件并寫入代碼
- 2.編譯.o文件并建立靜態庫文件鏈接
- 四、動態庫應用
- 1.建立文件并寫入代碼
- 2.建立動態庫文件鏈接
- 五、Linux GCC常用命令
- 1.創建示例程序
- 2.預處理
- 3.編譯為匯編代碼
- 4.匯編
- 5.連接
- 6.多個程序文件的編譯
- 7.檢錯
- 8.庫文件連接
- 六.nasm應用
- 1.安裝nasm
- 2.nasm使用
- 七.借助第三方庫函數完成代碼設計
- 1.光標庫 curses
- 2.體驗即將絕跡的遠古時代的 BBS
- 3.curses庫應用
- 4.貪吃蛇游戲
- 八.總結
一、可執行程序是如何被組裝的?
1.編輯:也就是編寫C/C++程序。
2.預處理:相當于根據預處理指令組裝新的C/C++程序。經過預處理,會產生一個沒有宏定義,沒有條件編譯指令,沒有特殊符號的輸出文件,這個文件的含義同原本的文件無異,只是內容上有所不同。
3.編譯:將預處理完的文件進行一系列詞法分析、語法分析、語義分析及優化后,產生相應的匯編代碼文件。
4.鏈接:通過鏈接器將一個個目標文件(或許還會有庫文件)鏈接在一起生成一個完整的可執行程序。 鏈接程序的主要工作就是將有關的目標文件彼此相連接,也就是將在一個文件中引用的符號同該符號在另外一個文件中的定義連接起來,使得所有的這些目標文件成為一個能夠被操作系統裝入執行的統一整體。在此過程中會發現被調用的函數未被定義。
二、用gcc生成靜態庫和動態庫
1.編輯生成例子程序
先創建一個作業目錄,保存本次練習的文件
mkdir test1 cd test1
這里使用vi命令創建并編輯3個所需文件:
程序 1: hello.h
#ifndef HELLO_H #define HELLO_H void hello(const char *name); #endif //HELLO_H程序 2: hello.c
#include <stdio.h> void hello(const char *name) { printf("Hello %s!\n", name); }程序 3: main.c
#include "hello.h" int main() { hello("everyone"); return 0; }2.將hello.c編譯成.o文件
無論靜態庫,還是動態庫,都是由.o 文件創建的。因此,我們必須將源程序 hello.c 通過 gcc 先編譯成.o 文件。
在系統提示符下鍵入以下命令得到 hello.o 文件。
然后用ls命令查看是否生成了hello.o文件
3.由.o 文件創建靜態庫文件
靜態庫文件名的命名規范是以 lib 為前綴,緊接著跟靜態庫名,擴展名為.a。
例如:我們將創建的靜態庫名為 myhello,則靜態庫文件名就libmyhello.a。在創建和使用靜態庫時,
需要注意這點。創建靜態庫用 ar 命令。在系統提示符下鍵入以下命令將創建靜態庫文件libmyhello.a。
然后用ls命令查看結果:
ls 命令結果中有 libmyhello.a
4.在程序中使用靜態庫
靜態庫制作完了,如何使用它內部的函數呢?
只需要在使用到這些公用函數的源程序中包含這些公用函數的原型聲明,然后在用 gcc 命令生成目標文件時指明靜態庫名,gcc 將會從靜態庫中將公用函數連接到目標文件中。
注意,gcc 會在靜態庫名前加上前綴 lib,然后追加擴展名.a 得到的靜態庫文件名來查找靜態庫文件。
運行結果如下
我們刪除靜態庫文件試試公用函數 hello 是否真的連接到目標文件 hello 中了
5.由.o 文件創建動態庫文件
動態庫文件名命名規范和靜態庫文件名命名規范類似,也是在動態庫名增加前綴 lib,但其文件擴展名為.so。例如:我們將創建的動態庫名為 myhello,則動態庫文件名就是 libmyhello.so。
用 gcc 來創建動態庫。
依然使用ls命令查看是否生成動態庫文件libmyhello.so
6.在程序中使用動態庫
在程序中使用動態庫和使用靜態庫完全一樣,也是在使用到這些公用函數的源程序中包含這些公用函數的原型聲明,然后在用 gcc 命令生成目標文件時指明動態庫名進行編譯。
我們先運行 gcc 命令生成目標文件,再運行它看看結果。
運行結果如下
程序在運行時,會在/usr/lib 和/lib 等目錄中查找需要的動態庫文件。若找到,則載入動態庫,否則將提示類似上述錯誤而終止程序運行。我們將文件 libmyhello.so 復制到目錄/usr/lib 中,再試試
這里我們需要在命令行前加入sudo,提供我們所需要的權限,否則無法進行文件的復制
成功了。這也進一步說明了動態庫在程序運行時是需要的。
我們回過頭看看,發現使用靜態庫和使用動態庫編譯成目標程序使用的 gcc 命令完全一樣,那當靜態庫和動態庫同名時,gcc 命令會使用哪個庫文件呢?抱著對問題必究到底的心情,來試試看。
先刪除除.c 和.h 外的所有文件,恢復成我們剛剛編輯完舉例程序狀態。
這里同樣使用了sudo,運行結果如下
再來創建靜態庫文件 libmyhello.a 和動態庫文件 libmyhello.so。
運行結果如下
通過上述最后一條 ls 命令,可以發現靜態庫文件 libmyhello.a 和動態庫文件 libmyhello.so 都已經生成,并都在當前目錄中。然后,我們運行 gcc 命令來使用函數庫 myhello 生成目標文件 hello,并運行程序 hello。
三、靜態庫應用
靜態庫應用內容:除了x2x函數之外,再擴展寫一個x2y函數(功能自定),main函數代碼將調用x2x和x2y ;將這3個函數分別寫成單獨的3個 .c文件,并用gcc分別編譯為3個.o 目標文件;將x2x、x2y目標文件用 ar工具生成1個 .a 靜態庫文件, 然后用 gcc將 main函數的目標文件與此靜態庫文件進行鏈接,生成最終的可執行程序,記錄文件的大小。
1.建立文件并寫入代碼
#gedit sub1.c #gedit sub2.c #gedit main.c
子函數sub1.c代碼如下
子函數sub2.c代碼如下
float x2y(float a, float b) {return a + b; }主函數main.c代碼如下
#include<stdio.h> extern float x2x(float a, float b); extern float x2y(float a, float b); int main() {float a = 2;float b = 3;printf("ax2xb=%f\n",x2x(a,b));printf("ax2yb=%f\n",x2y(a,b)); }2.編譯.o文件并建立靜態庫文件鏈接
#gcc -c sub1.c #gcc -c sub2.c #gcc -c main.c ls運行結果如下
由ar命令建立靜態庫
#ar -crv 1.a sub1.o sub2.o
調用sub1可執行程序結果如下
文件大小如下
四、動態庫應用
動態庫應用內容:將x2x、x2y目標文件用 ar工具生成1個 .so 動態庫文件, 然后用 gcc將 main函數的目標文件與此動態庫文件進行鏈接,生成最終的可執行程序,記錄文件的大小,并與之前做對比。
1.建立文件并寫入代碼
文件與代碼與(三)靜態庫應用的內容一致,此處我們將.a和可執行程序刪除后,進行動態庫文件的生成和應用
#rm 1.a #rm sub1 ls2.建立動態庫文件鏈接
由.o文件,用gcc創建動態庫文件
#gcc -shared -fPIC -o 2.so sub1.o sub2.o運行結果如下:
主函數與動態庫文件進行鏈接成可執行文件
運行結果如下
文件大小如下
總結:動態庫文件比靜態庫文件所占內存要小
五、Linux GCC常用命令
1.創建示例程序
//test.c #include <stdio.h> int main(void) { printf("Hello World!\n"); return 0; }2.預處理
gcc -E test.c -o test.i 或 gcc -E test.c可以輸出 test.i 文件中存放著 test.c 經預處理之后的代碼。打開 test.i 文件,看一看,就明白了。后面那條指令,是直接在命令行窗口中輸出預處理后的代碼. gcc 的-E 選項,可以讓編譯器在預處理后停止,并輸出預處理結果。在本例中,預處理結果就是將stdio.h 文件中的內容插入到 test.c 中了。
運行結果如下
3.編譯為匯編代碼
預處理之后,可直接對生成的 test.i 文件編譯,生成匯編代碼:
gcc -S test.i -o test.sgcc 的-S 選項,表示在程序編譯期間,在生成匯編代碼后,停止,-o 輸出匯編代碼文件。
4.匯編
對于上一小節中生成的匯編代碼文件 test.s,gas 匯編器負責將其編譯為目標文件,如下
gcc -c test.s -o test.o5.連接
gcc 連接器是 gas 提供的,負責將程序的目標文件與所需的所有附加的目標文件連接起來,最終生成可執行文件。附加的目標文件包括靜態連接庫和動態連接庫。對于上一小節中生成的 test.o,將其與C標準輸入輸出庫進行連接,最終生成程序 test
gcc test.o -o test運行結果如下
6.多個程序文件的編譯
通常整個程序是由多個源文件組成的,相應地也就形成了多個編譯單元,使用 GCC 能夠很好地管理
這些編譯單元。假設有一個由 test1.c 和 test2.c 兩個源文件組成的程序,為了對它們進行編譯,并最終生成可執行程序 test,可以使用下面這條命令:
如果同時處理的文件不止一個,GCC 仍然會按照預處理、編譯和鏈接的過程依次進行。如果深究起
來,上面這條命令大致相當于依次執行如下三條命令:
7.檢錯
gcc -pedantic illcode.c -o illcode-pedantic 編譯選項并不能保證被編譯程序與 ANSI/ISO C 標準的完全兼容,它僅僅只能用來幫助Linux 程序員離這個目標越來越近。或者換句話說,pedantic 選項能夠幫助程序員發現一些不符合ANSI/ISO C 標準的代碼,但不是全部,事實上只有 ANSI/ISO C 語言標準中要求進行編譯器診斷的那些情況,才有可能被 GCC 發現并提出警告。
除了-pedantic 之外,GCC 還有一些其它編譯選項也能夠產生有用的警告信息。這些選項大多以-W開頭,其中最有價值的當數-Wall 了,使用它能夠使 GCC 產生盡可能多的警告信息。
GCC 給出的警告信息雖然從嚴格意義上說不能算作錯誤,但卻很可能成為錯誤的棲身之所。一個優秀的 Linux 程序員應該盡量避免產生警告信息,使自己的代碼始終保持標準、健壯的特性。所以將警告信息當成編碼錯誤來對待,是一種值得贊揚的行為!所以,在編譯程序時帶上-Werror 選項,那么 GCC 會在所有產生警告的地方停止編譯,迫使程序員對自己的代碼進行修改,如下:
gcc -Werror test.c -o test運行結果
8.庫文件連接
編譯成可執行文件
首先我們要進行編譯 test.c 為目標文件,這個時候需要執行
鏈接
最后我們把所有目標文件鏈接成可執行文件:
Linux 下的庫文件分為兩大類分別是動態鏈接庫(通常以.so 結尾)和靜態鏈接庫(通常以.a 結尾),
二者的區別僅在于程序執行時所需的代碼是在運行時動態加載的,還是在編譯時靜態加載的。
強制鏈接時使用靜態鏈接庫
默認情況下, GCC 在鏈接時優先使用動態鏈接庫,只有當動態鏈接庫不存在時才考慮使用靜態鏈
接庫,如果需要的話可以在編譯時加上-static 選項,強制使用靜態鏈接庫。
在/usr/dev/mysql/lib 目錄下有鏈接時所需要的庫文件 libmysqlclient.so 和 libmysqlclient.a,為了讓
GCC 在鏈接時只用到靜態鏈接庫,可以使用下面的命令:
靜態庫鏈接時搜索路徑順序:
動態鏈接時、執行時搜索路徑順序:
六.nasm應用
1.安裝nasm
sudo apt-get install nasm//安裝nasm nasm -version//查看版本2.nasm使用
對示例代碼“hello.asm”編譯生成可執行程序,并與“hello world”C代碼的編譯生成的程序大小進行對比。
所需代碼:
編譯
鏈接
ld -s -o hello hello.o運行
./hello運行結果如下
生成文件的大小
此處為“hello world”的C代碼,經過gcc編譯生成的可執行程序的文件大小
總結:nasm的文件比gcc生成的文件要小
七.借助第三方庫函數完成代碼設計
1.光標庫 curses
| initscr() | 初始化curses庫和tty |
| endwin() | 關閉curses并重置tty |
| refresh() | 使屏幕按照你的意圖顯示 |
| move(r,c) | move(r,c) |
| addstr(s) | 在當前位置畫字符串s |
| addch(c) | 在當前位置畫字符c |
| clear() | 清屏 |
參考資料:
https://blog.csdn.net/akof1314/article/details/5948578
2.體驗即將絕跡的遠古時代的 BBS
3.curses庫應用
安裝curses庫
sudo apt-get install libncurses5-dev安裝過程遇到該錯誤
E: Unable to lock the administration directory (/var/lib/dpkg/), is another process using it?參考資料得以解決
https://blog.csdn.net/pfanaya/article/details/6695810
其頭文件被安裝在/usr/include
4.貪吃蛇游戲
建立可執行文件代碼如下
游戲如圖所示:
八.總結
學習過程艱難,花費時間較多,收獲良多,下次繼續努力QAQ
總結
以上是生活随笔為你收集整理的Ubuntu深入学习的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 简单的php 后台管理系统,GitHub
- 下一篇: 为什么跑完步了膝盖会疼痛?