C++編譯與連結(1)-編譯與連結過程
大家知道計算機使用的一系列的1和0
那個一個C++語言程式又是如何從一個個.h和.cpp檔案變成包含1和0的可執行檔案呢?
可以認為有以下的幾個環節
源程式->預處理->編譯和優化->生成目標檔案->連結->可執行檔案
1.預處理
C++的預處理是指在C++程式原始碼被編譯之前,由前處理器對C++程式原始碼進行的處理。這個過程並不對程式的原始碼進行解析。
這裡的前處理器(preprocessor)是指真正的編譯開始之前由編譯器呼叫的一個獨立程式。
前處理器主要負責以下的幾處
1.巨集的替換
2.刪除註釋
3.處理預處理指令,如#include,#ifdef
如我們有以下程式碼
temp.h
#ifndef _HEADERNAME_H #define _HEADERNAME_H 1 #include <iostream> inline void show(char *a) { std::cout << a<< std::endl;//annotation } #endif
main.cpp
#include "temp.h" #define MACRO "This is a macro" extern int i; int main() { std::cout<<i<<std::endl; show(MACRO); }
a.cpp
#include <iostream> int i=100;
*在vs2013中可以使用“VS2013 開發人員命令提示”
使用cl /P main.cpp只進行預編譯生成main.i檔案
*g++中可以使用(在以下只使用g++進行演示)
g++ –E main.cpp>main.i命令
g++ –E a.cpp>main.i
開啟生成的A.i檔案
我們發現
1、show函式中的註釋已經被刪掉了
2、main函式中的MACRO巨集被替成了"this is a macro”
windows vs下
3、temp.h和main.cpp中的#include<iostream> 和#include “temp.h”也在相應位置被展開了
2.編譯和優化
詞法分析 -- 識別單詞,確認詞類;比如int i;知道int是一個型別,i是一個關鍵字以及判斷i的名字是否合法
語法分析 -- 識別短語和句型的語法屬性;
語義分析 -- 確認單詞、短語和句型的語義特徵;
程式碼優化 -- 修辭、文字編輯;
程式碼生成 -- 生成譯文。
行內函數的替換就發生在這一階段
在g++中可以使用
g++ -S將預處理階段生成的.i檔案生成相應的彙編檔案
g++ –S main.i main.s
g++ –S a.i a.s
生成的部分程式碼如下:
3.生成目標檔案
彙編過程實際上指把組合語言程式碼翻譯成目標機器指令的過程。
在最終的目標檔案中
除了擁有自己的資料和二進位制程式碼之外,還要至少提供2個表:未解決符號表和匯出符號表,分別告訴連結器自己需要什麼和能夠提供什麼。
編譯器把一個cpp編譯為目標檔案的時候,除了要在目標檔案裡寫入cpp裡包含的資料和程式碼,還要至少提供3個表:未解決符號表,匯出符號表和地址重定向表。 未解決符號表提供了所有在該編譯單元裡引用但是定義並不在本編譯單元裡的符號及其出現的地址。 匯出符號表提供了本編譯單元具有定義,並且願意提供給其他編譯單元使用的符號及其地址。 地址重定向表提供了本編譯單元所有對自身地址的引用的記錄。
g++中可以使用g++ -c命令
g++ –c main.s –o main.o
g++ –c a.s –o a.o
4.連結
由彙編程式生成的目標檔案並不能立即就被執行,其中可能還有許多沒有解決的問題。例如,某個原始檔中的函式可能引用了另一個原始檔中定義的某個符號(如變數或者函式呼叫等);在程式中可能呼叫了某個庫檔案中的函式,等等。所有的這些問題,都需要經連結程式的處理方能得以解決。
g++ a.o main.o –o main.out
最終執行結果如下
100
This is a macro
參考文獻