链接工作过程

链接的作用是找到每个符号和函数的位置,并且将它们连接在一起。每个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;
}

Math.cpp

#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;
}

Log.cpp

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

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

Math.cpp

#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);

Log.cpp

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

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

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

这样编译链接也不会报错

 

posted @ 2020-03-31 16:45  Wangtn  阅读(220)  评论(0编辑  收藏  举报