前言:前面說了C/C++編譯的四大過程,本文繼續前面的文章來說明編譯過程中的注意事項,前面的系列文章請參考:
g++安裝配置以及C++的四個編譯處理過程(對比C#編譯執行過程)
VSCode開發C、C++環境搭建系列(一)——基于Mingw-w64搭建
一、GCC編譯的四大過程回顧
- 預處理(Pre-Processing):主要包括宏定義,檔案包含,條件編譯,注釋消除四部分。預處理過程讀入源代碼,檢查包含預處理指令的語句和宏定義,并對其進行響應和替換。預處理過程還會删除程式中的注釋和多餘空白字元。最後會生成
檔案。
.i
- 編譯器(Compiling):編譯器會将預處理完的
檔案進行一些列的文法分析,并優化後生成對應的彙編代碼。會生成
.i
檔案。
.s
- 彙編器(Assembling):彙編器會将編譯器生成的
彙程式設計式彙編為機器語言或指令,也就是可以機器可以執行的二進制程式。會生成
.s
檔案(MSVC是生成.obj檔案)。
.o
- 連結器(Linking):連結器會來連結程式運作的所需要的目标檔案,以及依賴的庫檔案,最後生成可執行檔案,以二進制形式存儲在磁盤中
從上面的四個過程可以得出,有兩個地方是需要用到外部資源的,第一個是頭檔案,第二個是庫檔案,包括靜态庫和動态庫。那麼編譯器在哪裡尋找這些檔案顯得很重要了,編譯器預設情況下都會有自己的查找規則和查找順序,當然我們也可以手動指定編譯器去哪裡查找。
注意事項:
雖然我們在編譯的時候隻使用了gcc這一個可執行程式,但是實際在執行的時候并不是隻有着一個可執行程式在執行,就像上面這幅圖所展示的,編譯過程是通過ccl.exe來完成的,而C++是通過cclplus.exe來完成的,彙編過程是通過as.exe來完成的,連結過程是通過ld.exe來完成的,其實在整個GCC套件裡面還有很多的可執行程式,這些可執行程式每一個都有自己的作用,都在不同的檔案夾中,GCC正是通過協調他們之間的工作與協作來實作完整的編譯過程的。
二、如何檢視GCC編譯器的預設搜尋路徑
我們可以通過制定GCC指令的參數來檢視,但是GCC現在的指令參數實在是太多了,現在有上千個指令參數,不可能每一個都記住,而且光是GCC的說明文檔有900多頁,也很多,我們掌握一些常見的即可,後面會專門出一篇關于GCC編譯器幾個重要的參數詳解。注意,本文的作業系統是 CentOS 7.4 和 Windows平台之下的Mingw-w64。
首先檢視一下幫助:
gcc --help
得到如下結果:
... ...省略了前面的
--version Display compiler version information
-dumpspecs Display all of the built in spec strings
-dumpversion Display the version of the compiler
-dumpmachine Display the compiler's target processor
-print-search-dirs Display the directories in the compiler's search path
-print-libgcc-file-name Display the name of the compiler's companion library
-print-file-name=<lib> Display the full path to library <lib>
-print-prog-name=<prog> Display the full path to compiler component <prog>
-print-multiarch Display the target's normalized GNU triplet, used as
a component in the library path
-print-multi-directory Display the root directory for versions of libgcc
-print-multi-lib Display the mapping between command line options and
multiple library search directories
-print-multi-os-directory Display the relative path to OS libraries
-print-sysroot Display the target libraries directory
-print-sysroot-headers-suffix Display the sysroot suffix used to find headers
... ...省略了後面的
2.1 關于GCC版本以及計算機的版本檢視
下面列舉三個最簡單的
(1)centos之下
[[email protected] ~]$ gcc --version # 檢視GCC編譯器的版本資訊
gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-39)
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
[[email protected] ~]$ gcc -dumpversion # 檢視版本資訊簡化版
4.8.5
[zoe@localhost ~]$ gcc -dumpmachine #檢視這台機器的資訊
x86_64-redhat-linux
(2)Mingw-w64之下
C:\Users\Administrator>gcc --version
gcc (x86_64-win32-seh-rev0, Built by MinGW-W64 project) 8.1.0
Copyright (C) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
C:\Users\Administrator>gcc -dumpversion
8.1.0
C:\Users\Administrator>gcc -dumpmachine
x86_64-w64-mingw32 # 這是Windows平台之下
2.2 檢視預設的搜尋路徑
gcc -print-search-dirs 或者是
g++ -print-search-dirs
執行這個指令會傳回三個結果:
(1)install: 路徑。即所謂的gcc安裝到哪一個位置的
(2)programs: 一系列路徑。這個指的是需要用到的一系列的可執行檔案的路徑,比如ccl.exe,cclplus.exe,as.exe,ld.exe等可執行程式在哪裡的路徑
(3)libraries: 一系列路徑。這裡指的就是頭檔案,靜态庫,動态庫的路徑所在位置
(1)CentOS 下檢視預設的搜尋路徑
我的電腦中GCC的可執行檔案在 /usr/bin/gcc ,而庫檔案在 /usr/lib/gcc/x86_64-redhat-linux/4.8.5/ 目錄之下,有以下一些檔案以及檔案夾:
得到如下結果
install: /usr/lib/gcc/x86_64-redhat-linux/4.8.5/
programs: =/usr/libexec/gcc/x86_64-redhat-linux/4.8.5/:
/usr/libexec/gcc/x86_64-redhat-linux/4.8.5/:
/usr/libexec/gcc/x86_64-redhat-linux/:
/usr/lib/gcc/x86_64-redhat-linux/4.8.5/:
/usr/lib/gcc/x86_64-redhat-linux/:
/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../x86_64-redhat-linux/bin/x86_64-redhat-linux/4.8.5/:
/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../x86_64-redhat-linux/bin/
libraries: =/usr/lib/gcc/x86_64-redhat-linux/4.8.5/:
/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../x86_64-redhat-linux/lib/x86_64-redhat-linux/4.8.5/:
/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../x86_64-redhat-linux/lib/../lib64/:
/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../x86_64-redhat-linux/4.8.5/:
/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/:/lib/x86_64-redhat-linux/4.8.5/:
/lib/../lib64/:
/usr/lib/x86_64-redhat-linux/4.8.5/:
/usr/lib/../lib64/:
/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../x86_64-redhat-linux/lib/:
/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../:/lib/:
/usr/lib/
總結:
/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../x86_64-redhat-linux/lib/x86_64-redhat-linux/4.8.5/:
# 其實要分成兩個來看
# /usr/lib/gcc/x86_64-redhat-linux/4.8.5/
# ../../../../x86_64-redhat-linux/lib/x86_64-redhat-linux/4.8.5/:
# 是以這兩個點表示的就是前面那個路徑之下的的 “上上” 一級目錄
# 後面的同樣的道理, ../../../ 表示的是多個上一級目錄
為什麼前面用一個路徑,然後後面有用一個路徑接着呢?
個人了解應該是前面的這個相當于是一個總的歸納,告訴後面的路徑到底是在哪一個基礎之上進行查找的。個人了解。
Linux下的搜尋路徑主要是在以下幾個地方進行查找的:
/usr/libexec/gcc/x86_64-redhat-linux/
/usr/lib/gcc/x86_64-redhat-linux/
/usr/lib/
/lib/
(2)Windows之下檢視
我的Windows平台上,Mingw-w64所安裝的目錄在 D:\Program Files\mingw64 目錄之下,包含了下面的内容
運作上面的指令之後的結果為:
install: D:/Program Files/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/8.1.0/
programs: =D:/Program Files/mingw64/bin/../libexec/gcc/x86_64-w64-mingw32/8.1.0/;
D:/Program Files/mingw64/bin/../libexec/gcc/;
D:/Program Files/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/8.1.0/../../../../x86_64-w64-mingw32/bin/x86_64-w64-mingw32/8.1.0/;
D:/Program Files/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/8.1.0/../../../../x86_64-w64-mingw32/bin/
libraries: =D:/Program Files/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/8.1.0/;
D:/Program Files/mingw64/bin/../lib/gcc/;
D:/Program Files/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/8.1.0/../../../../x86_64-w64-mingw32/lib/x86_64-w64-mingw32/8.1.0/;
D:/Program Files/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/8.1.0/../../../../x86_64-w64-mingw32/lib/../lib/;
D:/Program Files/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/8.1.0/../../../x86_64-w64-mingw32/8.1.0/;
D:/Program Files/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/8.1.0/../../../../lib/;
C:/mingw810/x86_64-810-win32-seh-rt_v6-rev0/mingw64/mingw/lib/x86_64-w64-mingw32/8.1.0/;
C:/mingw810/x86_64-810-win32-seh-rt_v6-rev0/mingw64/mingw/lib/../lib/;
D:/Program Files/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/8.1.0/../../../../x86_64-w64-mingw32/lib/;
D:/Program Files/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/8.1.0/../../../;
C:/mingw810/x86_64-810-win32-seh-rt_v6-rev0/mingw64/mingw/lib/
含義解釋:我們看到有這麼多的點,是一臉的迷茫,什麼意思呢?其實很簡單,以第一句話為例子:
D:/Program Files/mingw64/bin/../libexec/gcc/x86_64-w64-mingw32/8.1.0/;
# 其實要分成兩個來看
# D:/Program Files/mingw64/bin/
# D:/Program Files/mingw64/libexec/gcc/x86_64-w64-mingw32/8.1.0/;
# 是以這兩個點表示的就是前面那個路徑之下的的 “上上” 一級目錄
# 後面的同樣的道理, ../../../ 表示的是多個上一級目錄
我們發現主要是來自于兩個檔案夾之下:
即所謂的:
- lib檔案夾
- libexec檔案夾
- x86_64-w64-mingw32檔案夾之下
注意:上面的過程其實都是我的個人想法,不一定都是正确的,望有大佬告知,感激不盡。
2.3 GCC其他幾個指令的用法
-print-file-name=<lib> Display the full path to library <lib>
-print-prog-name=<prog> Display the full path to compiler component <prog>
(1)-print-prog-name參數的用法
這個參數實際上就是檢視GCC套件中的可執行程式(ccl.exe,cclplus.exe,ax.exe等可執行程式)的相關資訊的,使用方法如下
gcc -print-prog-name=<prog> //顯示可執行程式的名稱
gcc -print-prog-name=<prog> -v //顯示這個可執行程式的詳細資訊
`gcc -print-prog-name=<prog>` -v //顯示這個可執行程式的詳細資訊
前面介紹了,在GCC套件中,編譯的過程實際上是由ccl可執行程式來完成的,通過它ccl或者是cclplus我們可以檢視頭檔案在哪裡,在centos中,如下所示:
[[email protected] ~]$ `gcc -print-prog-name=cc1` -v
ignoring nonexistent directory "/usr/lib/gcc/x86_64-redhat-linux/4.8.5/include-fixed"
ignoring nonexistent directory "/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../x86_64-redhat-linux/include"
#include "..." search starts here:
#include <...> search starts here:
/usr/lib/gcc/x86_64-redhat-linux/4.8.5/include // C語言的頭檔案路徑
/usr/local/include
/usr/include
End of search list.
如果是執行cclplus得到:
[[email protected] ~]$ `gcc -print-prog-name=cc1plus` -v
ignoring nonexistent directory "/usr/lib/gcc/x86_64-redhat-linux/4.8.5/include-fixed"
ignoring nonexistent directory "/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../x86_64-redhat-linux/include"
#include "..." search starts here:
#include <...> search starts here: // C++的頭檔案路徑
/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../include/c++/4.8.5
/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../include/c++/4.8.5/x86_64-redhat-linux
/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../include/c++/4.8.5/backward
/usr/lib/gcc/x86_64-redhat-linux/4.8.5/include
/usr/local/include
/usr/include
End of search list.
三、手動指定GCC套件的搜尋路徑
手動指定頭檔案和庫檔案(靜态庫和動态庫)是通過指定gcc的兩個參數來完成的,如下:
- -Idir:将dir目錄加入搜尋頭檔案的目錄路徑(簡寫為-I)
- -Ldir:将dir目錄加入搜尋庫的目錄路徑(靜态庫和動态庫)(簡寫為-L)
下面是在gcc指令中手動設定搜尋路徑:
#添加頭檔案搜尋路徑
# gcc foo.c -I /home/xiaowp/include -o foo
#添加動态庫搜尋路徑
# gcc foo.c -L /home/xiaowp/lib -lfoo -o foo
注意:這裡的 -lfoo是什麼意思呢?它實際上是 動态庫 foo.so 的簡寫,我們給其添加一個字首 -l ,然後就不能在添加字尾 .so了,是以實際上等價于:
# gcc foo.c -L /home/xiaowp/lib foo.so -o foo
#添加靜态庫搜尋路徑
# gcc foo.c -L /home/xiaowp/lib -static -lfoo -o foo
注意:這裡的 -lfoo是什麼意思呢?它實際上是 動态庫 foo.a 的簡寫,我們給其添加一個字首 -l ,然後就不能在添加字尾 .a了,是以實際上等價于:
# gcc foo.c -L /home/xiaowp/lib -static foo.a -o foo
四、頭檔案、庫檔案搜尋順序總結
4.1 include頭檔案的搜尋路徑
- 目前目錄
- 在編譯的時候使用 -I參數 手動指定的路徑
- gcc環境變量設定CPATH ,C_INCLUDE_PATH ,CPLUS_INCLUDE_PATH ,OBJC_INCLUDE_PATH
- 系統标準include路徑。安裝GCC套件的時候可以通過--prefix參數手動指定安裝位置。
4.2 靜态庫(.a,.lib)
- gcc在編譯的時候通過 -L參數 手動指定的搜尋路徑
- 再找gcc的環境變量LIBRARY_PATH
- 再找内定目錄/lib:/usr/lib:/usr/local/lib這是這些安裝GCC套件的時候所安裝的那些目錄
4.3 動态庫(.so ,.dll )
- gcc在編譯的時候通過 -L參數 手動指定的搜尋路徑
- 環境變量LD_LIBRARY_PATH指定的動态庫搜尋路徑;
- 配置檔案/etc/ld.so.conf中指定的動态庫搜尋路徑;
- 再找内定目錄/lib:/usr/lib:/usr/local/lib這是這些安裝GCC套件的時候所安裝的那些目錄
參考文獻:
https://blog.51cto.com/13913090/2155979
https://www.linuxidc.com/Linux/2018-01/150640.htm
https://blog.csdn.net/seekkevin/article/details/50285495
http://blog.chinaunix.net/uid-26527046-id-4927986.html
https://www.linuxidc.com/Linux/2012-06/62552.htm