天天看點

連結工作過程

連結的作用是找到每個符号和函數的位置,并且将它們連接配接在一起。每個obj檔案之間是沒有聯系的,這些檔案實際上沒法互相溝通,是以需要将他們連接配接到一個程式中來。即使程式隻有一個cpp檔案,程式入口主函數和一些其他的東西仍需要被連結。

Math.cpp

#include<iostream>

void Log(const char* message)
{
    std::cout<<message<<std::endl;
}

int Multiply(int a,int b)
{
    Log("Multiply");
    return a*b;
}      

如果按ctrl+F7進行編譯,會編譯成功,但是按F5進行build會報錯:"entry point must be defined",因為缺少入口點main函數。

程式的聲稱是經過編譯和連結兩個步驟的,如果去掉函數的大括号,編譯時就會報錯,error後面會有C表示錯誤來自編譯,來自連結的錯誤在控制台中用LINK表示。

unresolved external symbo(未解決的外部符号),當連結器找不到他需要的東西時就會這樣報錯。

Log.cpp

#include<iostream>

void Logr(const char* message)
{
    std::cout<<message<<std::endl;
}      
#include<iostream>

void Log(const char* message);

int Multiply(int a,int b)
{
    Log("Multiply");
    return a*b;
}

int  main()
{
    std::cout<<Multiply(5,8)<<std::endl;
    std::cin.get();
}      

在連結時就會出現這種錯誤,如果在Multiply函數中注釋掉Log函數,那麼不會報錯,但是将main中的cout<<Multiply注釋掉,還是會報錯

如果在Multiply中删掉,那麼Log函數就成了徹底沒有被使用的死代碼了,但是第二種情況Log還是有可能被使用的,在其他的cpp檔案中,是以Linker需要連結他。

如果說明隻在Math.cpp檔案中使用Multiply函數,那麼可以消除Link的必要。改變方法就是将Multiply函數設成靜态

#include<iostream>

void Log(const char* message);

static int Multiply(int a,int b)
{
    Log("Multiply");
    return a*b;
}

int  main()
{
    //std::cout<<Multiply(5,8)<<std::endl;
    std::cin.get();
}      

這樣連接配接時就不會報錯

當兩個函數具有一樣的名字,類型和傳回值時,連結會出錯,因為Linker不知道該連結哪個,将Math.cpp修改

#include<iostream>

void Log(const char* message);

void Log(const char* message)
{
    std::cout<<message<<std::endl;
}

static int Multiply(int a,int b)
{
    Log("Multiply");
    return a*b;
}

int  main()
{
    //std::cout<<Multiply(5,8)<<std::endl;
    std::cin.get();
}      

此時連結就會出錯,因為Log函數已經在Log.cpp中被定義了,這樣會找到一個或多個被定義的符号。

常見錯誤

log.h

#pragma once

void Log(const char* message)
{
    std::cout<<message<<std::endl;
}      
#include<iostream>
#include"Log.h"

void InitLog()
{
    Log("Initialized Log");
}      
#include<iostream>
#include"Log.h"

static int Multiply(int a,int b)
{
    Log("Multiply");
    return a*b;
}

int main()
{
    std::cout<<Multiply(5,8)<<std::endll;
    std::cin.get();
}      

連結時出現錯誤,說Log在Log.obj中已經定義,在代碼中隻有log.h定義了Log函數,為什麼連結時會報錯定義多次呢?

因為在include時,頭檔案的内容會放到include語句的位置上,是以上述代碼中,Log函數分别在Log.cpp和Math.cpp中定義了兩次

那麼怎麼解決呢?将log.h中的Log設定成靜态

#pragma once

static void Log(const char* message)
{
    std::cout<<message<<std::endl;
}      

這意味着函數連結時連結隻應該發生在檔案内部,是以Math.cpp和Log.cpp都會有自己版本的Log函數,它對其他obj檔案都是不可見的

另一個改正方法是使用内聯函數inline

#pragma once

inline void Log(const char* message)
{
    std::cout<<message<<std::endl;
}      

inline的意思是把函數的身體直接拿過來取代調用,在這種情況下,Log.cpp中代碼相當于這樣

#include<iostream>
#include"Log.h"

void InitLog()
{
    std::cout<<"Initialized Log"<<std::endl;
}      

InitLog函數内部調用的Log函數直接變成了Log函數的結構。

也可以改變Log函數的位置

Log.h

#pragma once

inline void Log(const char* message);      
#include<iostream>
#include"Log.h"

void InitLog()
{
    Log("Initialized Log");
}

void Log(const char* message)
{
    std::cout<<message<<std::endl;
}