GCC編譯器背後的故事

iostreamhvoid 發表於 2020-10-17

一、用GCC生成.a靜態庫和.so動態庫

1、建立一個 test2 資料夾,並在該資料夾中建立三個子程式 hello.h、hello.c 和 main.c(這裡還需要下vim)
在這裡插入圖片描述
解決後,在vim中輸入我們建立的三個字程式程式碼

#ifndef HELLO_H 
#define HELLO_H 
void hello(const char *name); 
#endif //HELLO_H

#include <stdio.h> 
#include "hello.h"
void hello(const char *name) 
{
 printf("Hello %s!\n", name);
}

#include "hello.h" 
int main() 
{
 hello("everyone"); 
 return 0; 
}

如下(程式main.c為例)
在這裡插入圖片描述
在這裡插入圖片描述2、後將hello.h編譯成.o檔案,但是這裡需要安裝gcc

在這裡插入圖片描述
(安裝過程同上面vim差不多,有點多這裡就不放出來了)
在這裡插入圖片描述
3、由 .o檔案建立靜態庫,並在程式中使用
a…o檔案建立靜態庫
在這裡插入圖片描述
b. 在程式中使用靜態庫
方法一
gcc -o hello main.c -L. -lmyhello
方法二
gcc main.c libmyhello.a -o hello
方法二
gcc -o main.c # 先生成 main.o
gcc -o hello main.o libmyhello.a
然後./hello,執行程式
在這裡插入圖片描述
我們可嘗試刪除 libmyhello靜態庫,再次執行 hello 程式(看程式執行時,是否需要該靜態庫)

在這裡插入圖片描述
結果:靜態庫在程式編譯時會被連線到目的碼中,程式執行時將不再需要該靜態庫。
4、由 .o檔案建立動態庫,並在程式中使用
a. .o檔案建立動態庫
在這裡插入圖片描述
b. 在程式中使用動態庫

方法一

gcc -o hello main.c -L. -lmyhello

方法二

gcc main.c libmyhello.so -o hello

在這裡插入圖片描述
(原因:程式在執行時, 會在/usr/lib 和/lib 等目錄中查詢需要的動態庫檔案。若找到,則載入動態庫,否則將提示類似上述錯誤而終止程式執行)
解決方法:將檔案 libmyhello.so 移動到目錄/usr/lib 中
在這裡插入圖片描述

二、靜態庫與動態庫生成執行檔案大小比較

1、建立一個 test3資料夾,並在該資料夾中分別建立子程式 sub1.h、sub1.c、sub2.h、sub2.c、main.c
mkdir test3
cd test3
vim sub1.h
vim sub1.c
vim sub2.h
vim sub2.c
vim main.c
然後在vim輸入他們的程式程式碼,同上

#ifndef SUB1_H
#define SUB1_H 
float x2x(int a, int b);
#endif //SUB1_H

#include"sub1.h"
float x2x(int a,int b)
{
return a+b;
}

#ifndef SUB2_H
#define SUB2_H 
float x2y(int a, int b);
#endif //SUB2_H

#include"sub2.h"
float x2y(int a,int b)
{
return a*b;
}

#include<stdio.h>
#include"sub1.h"
#include"sub2.h"
int main(){
int a=2,b=3;
	printf("%d + %d = %f\n", a, b, x2x(a, b));
	printf("%d × %d = %f\n", a, b, x2y(a, b));
	return 0;

}

以第一個為例,輸入過程如下
在這裡插入圖片描述
在這裡插入圖片描述
2、用靜態庫檔案進行連結,生成可執行檔案
a. 將 sub1.c、sub2.c 編譯成 .o檔案
在這裡插入圖片描述
b. .o檔案建立靜態庫
在這裡插入圖片描述

c. 在程式中使用靜態庫
在這裡插入圖片描述
3、用動態庫檔案進行連結,生成可執行檔案
a. .o檔案建立動態庫
在這裡插入圖片描述
b. 在程式中使用動態庫
在這裡插入圖片描述
(在這之前,顯示了一次錯誤,說找不到目錄,然後再試了一遍,就可以了)
d. 兩個可執行檔案大小的比較

gcc -static main.c libsub1.a libsub2.a -o main1 # 重新由靜態庫生成
size main1
ldd main1
size main2
ldd main2

在這裡插入圖片描述

三、gcc編譯器是怎麼編譯的

1、建立一個 test0 資料夾,並在該資料夾中建立一個 hello.c 程式
程式碼如下

#include<stdio.h>
int main (void)
{
 printf("Hello World!\n");
 return 0;
}

2、程式的編譯過程在這裡插入圖片描述
a. 預編譯(將原始檔 hello.c 檔案預處理生成 hello.i)
b. 編譯(將預處理生成的 hello.i 檔案編譯生成彙編程式 hello.s)
c. 彙編(將編譯生成的 hello.s 檔案彙編生成目標檔案 hello.o)
d. 連結(分為靜態連結和動態連結,生成可執行檔案)
如下圖
在這裡插入圖片描述
e. 用 size 檢視檔案大小,ldd連結了那些動態庫
在這裡插入圖片描述
3、ELF 檔案的分析
(1) .text:已編譯程式的指令程式碼段
(2) .rodata:ro 代表 read only,即只讀資料(譬如常數 const)
(3) .data:已初始化的 C 程式全域性變數和靜態區域性變數
(4) .bss:未初始化的 C 程式全域性變數和靜態區域性變數
(5) .debug:除錯符號表,偵錯程式用此段的資訊幫助除錯
在這裡插入圖片描述
b. 反彙編 ELF
objdump -S 將其反彙編並且將其 C 語言原始碼混合顯示出來:
在這裡插入圖片描述
4、在ubuntu中下載安裝nasm,對示例彙編程式碼“hello.asm”編譯生成可執行程式,並與上述用C程式碼的編譯生成的可執行程式大小進行對比
a. 安裝nasm編譯器
下載 NSAM 軟體包:連結: link.
輸入sudo apt install nasm

在這裡插入圖片描述
輸入nasm -version

在這裡插入圖片描述
b. 編譯彙編 hello.asm檔案,並於C程式碼的編譯生成的程式大小進行對比
hello.asm 內容如下:
在這裡插入圖片描述

編譯
nasm -f elf64 hello.asm
連結
ld -s -o hello hello.o

在這裡插入圖片描述
c. 彙編與C程式碼的編譯生成的可執行程式大小對比
在這裡插入圖片描述

四、瞭解實際程式是如何藉助第三方庫函式完成程式碼設計

(一)、以遊客身份體驗一下即將絕跡的遠古時代的BBS
在win10下,開啟控制皮膚——>程式——>啟用或關閉Windows功能,啟動"telnet client" 和"適用於Linux的Windows子系統"如圖
在這裡插入圖片描述
b. 開啟一個 cmd命令列視窗,輸入如下命令: telnet bbs.newsmth.net
在這裡插入圖片描述
2、Linux 環境下C語言編譯實現貪吃蛇遊戲
a. 瞭解Linux 系統中終端程式最常用的游標庫(curses)
initscr(): initscr() 是一般 curses 程式必須先呼叫的函式, 一但這個函式被呼叫之後, 系統將根據終端機的形態並啟動 curses 模式
endwin(): curses 通常以呼叫 endwin() 來結束程式. endwin() 可用來關閉curses 模式, 或是暫時的跳離 curses 模式
refresh(): refresh() 為 curses 最常呼叫的一個函式
move(y,x): 將遊標移動至 x,y 的位置
echochar(ch)/addch(ch): 顯示某個字元
b. Ubuntu18.04 安裝curses庫
可通過 whereis 命令標頭檔案和庫檔案都被安裝到哪些目錄中:
c. Linux 環境下C語言編譯實現貪吃蛇遊戲

mkdir testSnake
cd testSnake
vim mysnake.c
gcc mysnake.c -lcurse -o mysnake
./mtsnake

./mysnake 執行該程式,效果如下:
在這裡插入圖片描述

五、參考連結

連結: link.