前言:
? ? ? ? 上文我們學到了,LInux中的的編輯器vim【Linux】vim編輯器-CSDN博客
? ? ? ? 本文來學習LInux中的編譯器:gcc/g++
? ? ? ? gcc是C語言編譯器,g++是C++編譯器,這兩個的使用一模一樣。這里我們主要使用gcc給大家介紹
?1.格式
gcc? 被編譯的源文件? ?選項? ?編譯的目標文件
選項:-o,生成指定文件
若不指定文件則默認生成a.out,若自定則生成指定的可執行文件
hyc@hcss-ecs-4ce7:~$ ls
new new.c#未指定
hyc@hcss-ecs-4ce7:~$ gcc new.c
hyc@hcss-ecs-4ce7:~$ ls
a.out new new.c
hyc@hcss-ecs-4ce7:~$ ./a.out
存在
存在
存在
存在
存在#指定
hyc@hcss-ecs-4ce7:~$ gcc new.c -o new.exe
hyc@hcss-ecs-4ce7:~$ ls
new new.c new.exe
hyc@hcss-ecs-4ce7:~$ ./new.exe
存在
存在
存在
存在
存在
2.編譯的過程?
2.1預處理
預處理的功能包括:替換宏定義、頭文件展開、條件編譯、去注釋等
預處理的指令是從#開始的代碼行
.c文件經過預處理會形成.i文件
-E選項:使編譯器處理完預處理就停下
hyc@hcss-ecs-4ce7:~$ ls
new new.chyc@hcss-ecs-4ce7:~$ gcc -E new.c -o new.i
hyc@hcss-ecs-4ce7:~$ ls
new new.c new.i
2.2編譯
編譯主要功能:檢查.i文件代碼是否規范,是否有語法錯誤。檢查無誤后gcc將代碼翻譯為匯編語言,生成.s文件
-S選項:使編譯器處理完編譯就停下
hyc@hcss-ecs-4ce7:~$ ls -l
total 28
drw-rwxr-x 2 hyc hyc 4096 May 26 15:41 new
-rw-rw-r-- 1 hyc hyc 219 May 29 21:58 new.c
-rw-rw-r-- 1 hyc hyc 18047 Jun 3 13:31 new.ihyc@hcss-ecs-4ce7:~$ gcc -S new.i -o new.s
hyc@hcss-ecs-4ce7:~$ ls -l
total 32
drw-rwxr-x 2 hyc hyc 4096 May 26 15:41 new
-rw-rw-r-- 1 hyc hyc 219 May 29 21:58 new.c
-rw-rw-r-- 1 hyc hyc 18047 Jun 3 13:31 new.i
-rw-rw-r-- 1 hyc hyc 895 Jun 3 13:38 new.s
補充:為什么要將代碼翻譯成匯編?
????????在計算機剛剛誕生的時代,沒有任何編程語言,科學家是通過計算機上的元器件開關來控制計算機的。后來科學家發明了”打孔編程“,通過紙袋傳遞二進制信息
? ? ? ? 再后來又發明了匯編語言,但匯編語言無法直接傳遞二進制信息。于是對于匯編語言的編譯器誕生了。通過編譯器將匯編語言映射為二進制,來操控計算機
? ? ? ? 再后來就出現了各種各樣的編譯性語言:例如C/C++,java,python等等等等。對于這些語言我們也需要將其翻譯為二進制,計算機才能明白我們的意圖。
? ? ? ? 但是這里有兩條路:C -> 二進制,C ->匯編?選著哪條?
? ? ? ? 顯然我們選著了第二條路,因為C語言到二進制這無疑就困難的。而C到匯編語言仍是文本上的翻譯,相對簡單。我們可以直接站在巨人的肩膀上不用做二進制的翻譯。
補充:編譯器的誕生
? ? ? ? 匯編語言被發明了,想要匯編語言被翻譯成二進制,那么需要一個編譯器來編譯匯編語言。那匯編語言的編譯器是怎么來的?答案是:先通過二進制編寫一個編譯器,得到匯編語言的編譯器后,再通過使用匯編語言寫一個匯編編譯器。最終得到一個匯編版的匯編編譯器。
? ? ? ? 同理C語言被發明了,想要有一個C語言的編譯器,只能先使用匯編語言編寫一個C語言編譯器,在通過C語言編譯器寫一個C語言的編譯器。最終得到一個C語言版的C語言編譯器。
? ? ? ? 這就是編譯器的自舉
2.3匯編
匯編主要功能:將.s文件轉化為機器可識別的二進制文件(.o)
-c選項:使編譯器處理完匯編就停下
hyc@hcss-ecs-4ce7:~$ ls
new new.c new.i new.shyc@hcss-ecs-4ce7:~$ gcc -c new.s -o new.o
hyc@hcss-ecs-4ce7:~$ ls -l
total 36
drw-rwxr-x 2 hyc hyc 4096 May 26 15:41 new
-rw-rw-r-- 1 hyc hyc 219 May 29 21:58 new.c
-rw-rw-r-- 1 hyc hyc 18047 Jun 3 13:31 new.i
-rw-rw-r-- 1 hyc hyc 1744 Jun 3 13:42 new.o
-rw-rw-r-- 1 hyc hyc 895 Jun 3 13:38 new.s
2.4連接
將庫方法與我們自己寫的目標文件連接起來,形成可執行文件
hyc@hcss-ecs-4ce7:~$ gcc new.o -o new.exe
hyc@hcss-ecs-4ce7:~$ ls -l
total 52
drw-rwxr-x 2 hyc hyc 4096 May 26 15:41 new
-rw-rw-r-- 1 hyc hyc 219 May 29 21:58 new.c
-rwxrwxr-x 1 hyc hyc 15960 Jun 3 13:51 new.exe
-rw-rw-r-- 1 hyc hyc 18047 Jun 3 13:31 new.i
-rw-rw-r-- 1 hyc hyc 1744 Jun 3 13:42 new.o
-rw-rw-r-- 1 hyc hyc 895 Jun 3 13:38 new.s
hyc@hcss-ecs-4ce7:~$ ./new.exe
存在
存在
存在
存在
存在
補充:
記憶選項:ESc
記憶文件后綴:.iso
對于編譯器來說一般都是先將所有的文件編譯為 .o文件,再將全部 .o文件一起連接
3.理解條件編譯
條件編譯是在預處理階段執行的,其具體操作是將不滿足條件的代碼直接抹去,只保留滿足條件的
演示:
新建一個代碼
保存并退出,我們讓其進行預處理,然后再查看代碼
hyc@hcss-ecs-4ce7:~$ gcc -E test.c -o test.i
hyc@hcss-ecs-4ce7:~$ vim test.i
我們可以看到代碼就只剩第一個printf函數了。
同時編譯器還支持在命令行中動態的定義宏
#使用-D選項,可以實現在命令行中動態的定義宏hyc@hcss-ecs-4ce7:~$ gcc test.c -o test
hyc@hcss-ecs-4ce7:~$ ./test
收費->專業版
hyc@hcss-ecs-4ce7:~$ gcc test.c -o test -DM=10
hyc@hcss-ecs-4ce7:~$ ./test
免費->社區版
預處理的本質就是編輯我們的代碼。像我們用到的vs、xshell等等工具都有社區版和專業版的區別,但不論是那個版本其本質都是同一個項目,只是使用了條件編譯將專業版進行閹割作為社區版供給普通人免費使用。
4.動態庫靜態庫
什么的庫?
????????庫是編程中的 “工具箱”,通過復用成熟代碼,讓開發者聚焦業務邏輯,大幅提升開發效率和代碼質量。從基礎的標準庫到專業的第三方庫,它們構成了現代軟件開發的核心生態,是實現快速迭代和復雜功能的關鍵支撐。
補充:ldd指令
ldd指令可以讓我們查看可執行程序依賴了那些庫
這里我們可以看到這個可執行程序依賴了C語言的動態庫。
庫的命名方式
動態庫 :
在代碼加載到內存時,會進行動態連接:將程序中要使用的庫實現函數,在庫中找到相應位置,并對函數地址進行替換。這樣在調用對應函數時,就會直接跳轉到庫中相應位置進行執行。執行完畢跳轉回來繼續向下執行。
靜態庫:
靜態庫與動態庫相反,在編譯階段就會將所需要部分的代碼,直接拷貝到我們自己的代碼中。
補充:
1.Linux下動態庫為:xxx.so? ? 靜態庫為:xxx.a
? ?windows下動態庫為:xxx.dll? ? 靜態庫為:xxx.lib
2.靜態庫只在連接時有用,一旦形成可執行文件后, 就不再需要了
動靜態庫對比
1.動態庫所形成的可執行文件,體積小
2.可執行文件對靜態庫依賴小,動態庫依賴大不可缺失
3.運行程序需要加載至內存中,靜態庫的連接方式會導致內存中出現大量重復代碼
4.動態庫的連接方式比較節省內存空間和磁盤資源
(一般的可執行程序都是默認動態連接的)
簡單理解庫的本質
庫分為兩部分:一個是開放的.h頭文件包含各個函數的聲明,另一個是的各個函數的實現,是編譯后的.o文件組成的集合