链接工作过程
链接的作用是找到每个符号和函数的位置,并且将它们连接在一起。每个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; }
这样编译链接也不会报错