標頭檔、編譯與連結
專案建置[6]
by 鹽亞倫
標頭檔
Headers
我們來看看上禮拜的code
可以發覺
宣告、實作與測試
全部都混再一起了
程式變的又臭又長
事實上,一個比較好專案當中
應該要把不同功能的程式碼放在不同的檔案當中
所以要怎麼做ㄌㄟ˙
假設今天有一個class a
其實我可以將a的實作部分另外放在一個檔案當中
class A{
int x, y, z;
void print();
}
int main() {
A a;
cin >> a.x >> a.y >> a.z;
a.print();
}
void A::print() {
cout << "("
cout << this->x << ", ";
cout << this->y << ", ";
cout << this->z << ")\n";
}
所以要怎麼做ㄌㄟ˙
假設今天有一個class a
其實我可以將a的實作部分另外放在一個檔案當中
class A{
int x, y, z;
void print();
}
void A::print() {
cout << "("
cout << this->x << ", ";
cout << this->y << ", ";
cout << this->z << ")\n";
}
class A{
int x, y, z;
void print();
}
int main() {
A a;
cin >> a.x >> a.y >> a.z;
a.print();
}
main.cpp
a.cpp
要注意的是,宣告必須在兩個地方都存在
可是假設我今天linked list的宣告那麼長
又要在很多個檔案裡面使用它
一直複製好麻煩?
因此,C/C++出現了標頭檔
標頭檔
- 副檔名 .h或是.hpp,幾乎沒有差別
- 一個把所有提前宣告的東西一起寫完的地方
- 用 #include 的方式把它 include 進來
- 例如:
#include <iostream>
#include "person.h"
-
其中,用<>的話代表是系統標頭檔
-
例如電腦
的/usr/bin/include中會有一個iostream.h
-
-
用 "" 的則是你自己寫的標頭檔
,要放在同一個目錄底下
來把上禮拜的Linked List的宣告變成標頭檔吧!
來研究一下我寫的haeder檔
#ifndef LINKEDLIST_H
#define LINKEDLIST_H
.......
#endif
這個是什麼?
這是為了防止被重複引用
利用巨集指令,確保只會被一個cpp檔案引用一次
才不會重複宣告
其他細節
extern -- 外部變數關鍵字
如果在一個.h檔或是.cpp檔中,
需要用到在另外一個.cpp檔案中宣告的全域變數
需要使用extern關鍵字代表你要引用他
Ex.
main.cpp (實際宣告處)
a.cpp (引用處)
int GlobleTime;
extern int GlobleTime;
編譯與連結
弄完剛剛的東西
你可能會發覺你不知道該怎麼編譯執行那麼多檔案了
所以現在來講講編譯器吧
編譯器工作流程
- 預處理
- 編譯
- 組譯
- 連結
g++ 基本用法
g++ 的 flags
-o
-c
-s
-shared
g++ CPP_NAME.cpp
執行檔檔名會是 a.out 或是 a.exe
一個一個慢慢來講吧
-std
-D
-g
-I
-l
-L
多檔案:
g++ FILE1.cpp FILE2.cpp FILE3.cpp
直接變成執行檔
首先是基本參數
基本上一定會加上這些
除錯用參數
-g
允許gdb相關功能
-Wall
顯示所有錯誤訊息
版本參數
-std
選擇c++版本,例如:
-std=c++11
巨集參數
-D
從這裡#define
東西,例如:
-DAaW
(相當於#define AaW)
我的常見g++用法:
g++ -g -Wall -DAaW -std=c++17 -o OUTPUTNAME.out FILENAME.cpp
檔名參數
-o
更改輸出檔名
Object Files
副檔名.o
編譯以及組譯完之後會產生的二進位檔
通常如果有多個cpp檔,會先一個一個編譯成.o之後,再連結成執行檔
如何生成object files
用 -c (小寫)
g++ -c -o haha.o hahaha.cpp
將hahaha.cpp編譯成haha.o
如果沒有用-o預設會和cpp檔同名
g++ -c hahaha.cpp
(生成hahaha.o)
將多個object file變成一個執行檔
g++ -o hahaha.exe a.o b.o c.o
透過以上方法就可以將多個檔案編譯並執行了
引用標頭檔
#include <> 只能include系統標頭檔
#include "" 只能include同個資料夾的
啊我如果想要include別的資料夾的怎麼辦?
.
├── build
├── include
│ ├── a.h
│ └── b.h
├── lib
└── src
└── a.cpp
用 -I 參數
例如:
g++ -I./include -o haha.exe a.cpp
這樣就可以在程式碼中用<>了
補充:編譯成組合語言
-s 參數
可以先把cpp檔變成組合語言
然後之後組合語言可以再用-c參數
組合成object files
我不會組合語言 去問溫室菜
library
Library
有一些程式碼,每一次要使用時都還要慢慢編譯,實在是太慢了
例如,整個STL加起來可能有幾萬行
不可能每次重新編譯
因此有個東西叫做函式庫(library)
本身就是二進位模式,編譯時可以直接和object files 連結,以減少重新編譯時間
靜態vs動態函式庫
(Static library)
- 檔案名稱以 lib 開頭,而副檔名則為 .a。
- 在程式變為執行檔時便載入完成
- 具有三個優點
- 較高的執行速度
- 只要保證使用者有程式對應的函式庫,便能執行
- 避免因為程式找不到.dll而無法執行(dll地獄)
編譯成靜態函式庫
編譯成動態函式庫
-shared
例如
$ gcc -c -o hello.o hello.c
$ gcc -shared -o libhello.so hello.o
$ gcc -c -o sum.o sum.c
$ ar -rcs libsum.a sum.o
-l 和 -L 參數
使用靜態函式庫
-L(大寫):資料夾
-l(小寫):檔案名
假設資料夾長這樣(pwd在build)
.
├── build
│ └── a.o
├── include
│ ├── a.h
│ └── b.h
├── lib
│ └── libhahaha.a
└── src
└── a.cpp
而我想要將a.o和hahaha函式庫編譯在一起
$ gcc -o eee.exe -L../lib -lhahaha a.o
$ gcc -o eee.exe ../lib/libhahaha.a a.o
或是
實作時間
寫一個build.sh來練習編譯吧
延伸資料:
- https://ithelp.ithome.com.tw/articles/10257387
- https://blog.gtwang.org/programming/gcc-comipler-basic-tutorial-examples/
- https://tw511.com/a/01/3167.html
- https://medium.com/fcamels-notes/linux-%E7%B7%A8%E8%AD%AF-shared-library-%E7%9A%84%E6%96%B9%E6%B3%95%E5%92%8C%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A0%85-cb35844ef331
- https://blog.gtwang.org/programming/howto-create-library-using-gcc/
下課
是不是有人還沒備明天ㄉ演算法啊
話說