天天看點

C++内部連結與外部連結

你曾經碰到的問題: 

1.為什麼有時會出現aaa已在bbb中重定義的錯誤?

2.為什麼有時會出現無法解析的外部符号?

3.為什麼有的内聯函數的定義需要寫在頭檔案中?

4.為什麼對于模闆,聲明和定義都要寫在一起?

編譯單元

 什麼是編譯單元呢?簡單來說一個cpp檔案就是一個編譯單元。

編譯單元:當一個c或cpp檔案在編譯時,預處理器首先遞歸包含頭檔案,形成一個含有所有 必要資訊的單個源檔案,這個源檔案就是一個編譯單元。

事實上編譯每個編譯單元(.cpp)時是互相獨立的,即每個cpp檔案之間是不知道對方的存在的(不考慮#include “xxx.cpp" 這種奇葩的寫法)

編譯器會分别将每個編譯單元(.cpp)進行編譯,生成相應的obj檔案

然後連結器會将所有的obj檔案進行連結,生成最終可執行檔案。

内部連結與外部連結

那麼什麼内部連結和外部連結又是什麼呢?

我們知道C++中聲明和定義是可以分開的

例如 我們可以一個函數聲明定義放在b.cpp中,在a.cpp隻需再聲明一下這個函數,就可以在a.cpp中使用這個函數了

a.cpp

C++内部連結與外部連結
void show();

int main()
{
      show();
     return 0;
}      
C++内部連結與外部連結

b.cpp

#include <iostream>
void show()
{
    std::cout << "Hello" << std::endl;
}      

而通過之前的了解,我們知道每個編譯單元間是互相獨立不知道彼此的存在的

那麼a.cpp又是如何知道show函數的定義的呢

其實在編譯一個編譯單元(.cpp)生成相應的obj檔案過程中

編譯器會将分析這個編譯單元(.cpp)

将其所能提供給其他編譯單元(.cpp)使用的函數,變量定義記錄下來。

而将自己缺少的函數,變量的定義也記錄下來。

是以可以認為a.obj和b.obj記錄了以下的資訊

C++内部連結與外部連結

然後在連結器連接配接的時候就會知道a.obj需要show函數定義,而b.obj中恰好提供了show函數的定義,通過連結,在最終的可執行檔案中我們能看到show函數的運作。

好了讓我們看下 内部連結和外部連結比較正式的定義吧 

内部連接配接:如果一個名稱對編譯單元(.cpp)來說是局部的,在連結的時候其他的編譯單元無法連結到它且不會與其它編譯單元(.cpp)中的同樣的名稱相沖突。例如static函數,inline函數等(注 : 用static修飾的函數,本限定在本源碼檔案中,不能被本源碼檔案以外的代碼檔案調用。而普通的函數,預設是extern的,也就是說,可以被其它代碼檔案調用該函數。)

外部連接配接:如果一個名稱對編譯單元(.cpp)來說不是局部的,而在連結的時候其他的編譯單元可以通路它,也就是說它可以和别的編譯單元互動。 例如變量就是外部連結, 全局變量。

那麼回到最初的問題:

1. 為什麼有時會出現aaa已在bbb中重定義的錯誤?

答:  你可能在不同的cpp中重複定義了一個具有外部連結的函數或變量,連結器在連結時找到了多個一樣的函數或變量定義。

2. 為什麼有時會出現無法解析的外部符号?

答:你可能隻提供了函數或變量的聲明,沒有提供其定義,或者聲明和定義的函數原型不一緻,連結器沒有找到其定義在哪裡,是以在連結環節出現了無法解析的外部符号的錯誤。

3. 為什麼有的内聯函數的定義需要寫在頭檔案中呢?

答:因為内鍊函數是内部連結的,如果你在b.cpp中定義這個函數,那麼在a.cpp中即使有這個函數聲明,但由于内聯函數是内部連結的,是以b.cpp不會提供其定義。是以在連結時a.obj無法找到這個函數的定義,便會出現無法解析的外部符号的錯誤

4.為什麼對于模闆,聲明和定義都要寫在一起呢?

答:我們假設我們有如下結構的代碼

a.h

C++内部連結與外部連結
#pragma once
template<typename T>
class A
{
public:
    A(const T &t);
};      
C++内部連結與外部連結

a.cpp

C++内部連結與外部連結
#include "a.h"
#include <iostream>

template<typename T>
A<T>::A(const T &t)
{
    std::cout << t << std::endl;
}      
C++内部連結與外部連結

main.cpp

C++内部連結與外部連結
#include "a.h"

int main()
{ 
    A<int> a(5);
    return 0;
}      
C++内部連結與外部連結

 那麼程式能否正常運作呢?答案是不能 我們首先來分析一下編譯器在編譯main.cpp時,隻有聲明,發現其缺少A<int>::a(const int& t)的定義 ,因為它不在a.h裡面, 于是編譯器隻好寄希望于連接配接器, 希望它能夠在其他.obj裡找到定義, 而在編譯器編譯a.cpp時,沒有用到A<int> , 模闆隻有被用到的時候才會被執行個體化, 每個編譯單元是獨立的,它也不知道main.cpp用了A<int> ,是以它不會提供定義,編譯出來的a.obj檔案中關于A 的一行二進制代碼也沒有,這樣在連結時main.obj無法找到A<int>::a(const int& t)的定義,就會出現無法解析的外部符号的錯誤LNK1120 (注意, 在a.cpp 中加入一個函數用到A, void f2(){ A<int >  a(222); } ,則此問題解決  )

5.宏是内部連結還是外部連結

答:都不是,宏在預處理環節時就被替換掉了,而内部連結與外部連結是針對編譯環節與連結環節而言的

Created by 黃強

C++内部連結與外部連結

繼續閱讀