C++[1] 編譯和連接

單文件例子

先以單文件編譯爲例子 hello.cc:html

#include<iostream>
using namespace std;

int main() {
    cout << "Hello World" << endl;
    return 0;
}

編譯加運行的命令ios

g++ 'hello.cc' -o 'hello' -Wall -g -O2  -std=c++17 && hello

其含義就是將hello.cc源文件編譯輸出爲hello這個可執行文件並運行, 獲得一個"Hello World"的輸出, 那麼這個簡單的 g++ 命令背後發生了什麼呢?c++

編譯過程

上述gcc命令其實依次執行了四步操做:shell

1.預處理(Preprocessing)macos

2.編譯(Compilation)測試

3.彙編(Assemble)ui

4.連接(Linking)spa

image.png

常見後綴含義

.c爲後綴的文件:c語言源代碼文件.net

.a爲後綴的文件:是由目標文件構成的庫文件code

.cpp爲後綴的文件:是c++源代碼文件

.h爲後綴的文件:頭文件

.o爲後綴的文件:是編譯後的目標文件

.s爲後綴的文件:是彙編語言源代碼文件

.m爲後綴的文件:Objective-C原始程序

.so爲後綴的文件:編譯後的動態庫文件

1.預處理(Preprocessing)

預處理階段作的事情: 宏的替換,還有註釋的消除,還有找到相關的庫文件,將#include文件的所有內容插入。若用<>括起文件則在系統的INCLUDE目錄中尋找文件,若用""括起文件則在當前目錄中尋找文件。預處理以後獲得的仍然是文本文件,但文件體積會大不少, 生成的文件是 -i 文件, 對hello.cc文件進行預處理的命令

g++ -E hello.cc -o hello.i

或者直接調用cpp命令

cpp hello.cc -o hello.i

-E是指讓編譯器在預處理以後就退出,不進行後續編譯過程, -o是指指定輸出文件名, 在處理成.i文件之後, 文件的體積會大的多, 變成了4w多行的文件

2.編譯(Compilation)

這裏的編譯不是指程序從源文件到二進制程序的所有過程,而是指將通過預處理以後的程序轉換成特定彙編代碼(assembly code)的過程。將.i預處理文件編譯爲彙編代碼的命令是cc1, 使用-S能夠直接從.cc文件到彙編代碼

g++ -S hello.cc -o hello.s

hello.s文件的部份內容

.section    __TEXT,__text,regular,pure_instructions
    .build_version macos, 10, 14    sdk_version 10, 14
    .globl    _main                   ## -- Begin function main
    .p2align    4, 0x90
_main:                                  ## @main
    .cfi_startproc
## %bb.0:
    pushq    %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset %rbp, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register %rbp
    subq    $16, %rsp

3.彙編(Assemble)

彙編過程將上一步的彙編代碼轉換成機器碼(machine code),這一步產生的文件叫作目標文件,是二進制格式。gcc彙編過程經過as命令完成:

as hello.s -o hello.o

等價於

g++ -c hello.s -o hello.o

輸出的hello.o爲二進制文件
這一步會爲每個源文件產生一個目標文件

4.連接(Linking)

連接過程將多個目標文件以及所需的庫文件(.so等)連接成最終的可執行文件(executable file)
命令大體以下

ld -o test.out test.o inc/mymath.o ...libraries...

因此其實簡單的一行命令

g++ hello.cc

其中其實包含了好幾層流程

多文件例子

添加兩個文件 math.h

`int add(int a, int b);`

math.cc, 定義一下這個add方法

int add(int a, int b) {
    return a + b;
};

而後在hello.cc中調用一下這個add方法

#include<iostream>
#include "math.h"
using namespace std;

int main() {
    cout << "Hello World" << endl;
    cout << add(1, 2) << endl;
    return 0;
}

再直接運行

g++ hello.cc

試試, 發現收到以下報錯

Undefined symbols for architecture x86_64:
  "add(int, int)", referenced from:
      _main in hello-9842e2.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

看到說add(int, int)這個方法是未定義符號, 但是明明將math.h引入進來了啊, 實際上是由於沒有連接到math.cc生成的.o文件, 連接math.o再生成可執行文件試試

g++ -c hello.cc math.cc

這樣生成了hello.o math.o兩個二進制文件, 而後連接math.o和hello.o生成可執行文件

g++ hello.o math.o -o hello

再運行hello試試, 發現獲得了想要的輸出。或者還有一個更簡單的辦法

g++ hello.cc math.cc -o hello

通過我測試, 能夠直接獲得上面兩個命令合併的效果。
上面的命令, 在中大型項目中, 通常使用makefile文件進行整合, makefile文件的做用和寫法單獨寫一個文章。

參考連接

https://www.cnblogs.com/ericl...
https://blog.csdn.net/csdn912...

相關文章
相關標籤/搜索