cilinking:从C++源码到可执行二进制的过程。compile文件之后进行链接,找到每个符号、函数的位置,并将其链接在一起
每个文件被编译成一个独立的.obj文件作为translation unit,这些文件无法自主沟通。写一个程序使用到了多个文件时,使用linker将这些文件链接到一个程序。(<——链接器的工作)。
即使没有外部文件里的函数(仅有一个文件),应用程序为了直到入口点在哪(main函数在哪,程序运行时从main函数开始)
编译有两个阶段:编译与链接
区分方法:ctrl+F7 或 build(编译)文件,仅有编译发生,未发生链接
build整个项目之后,或F5运行时 先编译后链接
编译器complier处理语法错误 C开头, 表示编译阶段发生的错误
链接器linker处理链接错误 LNK开头, 表示链接阶段发生的错误
令Source Files文件夹下仅有Math.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;
}
此时build整个项目会报错LINK:fatal error LNK1561:entry point must be defined
原因:缺少主函数
.exe文件必要要有一个切入点
该切入点不一定非得是main函数,但是必须要有切入点
在Math.cpp函数中添加main函数,编译整个项目成功(linker)生成.exe文件,此时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;
}
int main()
{
}
再次修改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;
}
int main()
{
std::cout << Multiply(5,8) << std::endl;
std::cin.get();//该语句确保控制台不会被瞬间关闭
}
输出
假设这些程序存在于多个文件中:(将重新添加Log函数.cpp文至Source Files),将Math.cpp文件中的Log函数移至Log.cpp文件在,重新编译Math.cpp会得到C开头的error,没有找到Log,原因是Math.cpp不知道Log函数的存在。Math.cpp文件中并未添加Log函数的声明
将Log函数的首行添加至Math.cpp中进行声明,再次编译显示成功
此时build整个项目,会报compile error
原因:Log函数中并未include <iostream>
Log函数中include <iostream>后,项目compile(linker)成功
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();//该语句确保控制台不会被瞬间关闭
}
Log.cpp
#include <iostream>
void Log(const char* message)
{
std::cout << message << std::endl;
}
常见的linking error(链接错误)
①uresolved external symbol(未解决的外部符号)
原因:链接器找不到需要的东西
例如,此时将Log文件中"void Log(const char* message) "Log进行改变,Math.cpp文件中仍然有Log函数的声明,文件仍然会被compile编译(因为没有linker,文件只检查以确保能够正确编译,相信某处有Log函数,但是找到这个函数是链接阶段的工作),此时compile整个项目(linker),会得到linking错误,uresolved external symbol(未解决的外部符号),那个符号缺失了(此处为Log),在何处引用该函数
若将Math.cpp文件中的Log函数注释掉进行编译,linker不会报错,因为linking过程中,并没有使用Log函数,所以原本的Log.cpp如和改变并不影响,因为Math.cpp根本没有使用Log函数,它被注释掉了
若将Math.cpp文件main函数中的Multiply与Log函数同时注释掉(仅需要注释掉含Multiply的语句,因为Log是被Multiply调用的),build项目,可能会得到一个linking error,可能的原因是:虽然文件中没有使用Multiply函数,技术上来讲可能在另一个文件中使用它,因此linker确实需要linking该函数。若有办法告诉编译器该函数——Multiply——仅仅会在此文件中使用,就可以去掉这种linking error(Multiply从未被调用,所以也永远不会调用Log),实现方法:Multiply前加上static,意味着这个Multiply函数只为该单元声明(也就是该例中的Math.cpp文件),此时linking将不会报错。
Math.cpp
#include <iostream>
void Log(const char* message);
static int Multiply(int a, int b)//linking报错时需要添加static
{
Log("Multiply");
return a * b;
}
int main()
{
//std::cout << Multiply(5,8) << std::endl;//Multiply被注释掉
std::cin.get();
}
一切回复正常后,再将Log函数的返回值类型发生改变。Log.cpp:
#include <iostream>
int Log(const char* message) //由void改为int
std::cout << message << std::endl;
return 0;//此句不加会产生C error
}
再次build项目,linking error:
改变Log函数中的参数,Log.cpp:
#include <iostream>
void Log(const char* message, int level)
{
std::cout << message << std::endl;
}
build项目将会再次报错,因为linker链接器要寻找的Log函数并没有另一个参数,仅有一个参数const char *,如果链接器找不到一摸一样的,就会产生linking error
②有重复符号(函数或变量有相同的名字和相同的签名)时,两个相同名称的函数具有相同的返回值和相同的参数。此时linker不明确那个link到那个。
Log.cpp函数中重复再写void Log内容后进行编译,编译器会报错:error C2084: 函数“void Log(const char *)”已有主体
即,函数中有重复符号时,编译器会报错,因为都在一个文件中,而不需要链接发生就可以判断。若将重复内容移至其他文件(例Math.cpp),此时编译文件不会出错,但是build项目将会报错,linking error:Log已被定义。链接器不知道该链接至那个函数。
发生可能:
文件复原可对项目成功build之后,新建header file,Log.h
Log.h中进行Log函数的定义,而Log.cpp中仅进行声明
Math.cpp中的Multiply函数中调用log,Log.cpp的InitLog函数中也调用
//Log.cpp
#include <iostream> //include后,error消失
#include "Log.h"
void InitLog()
{
Log("Initialized Log");
}
//Log.h
#pragma once
void Log(const char* message)
{
std::cout << message << std::endl;
}
//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::endl;
std::cin.get();
}
此时build整个项目,会报错,linking error
此时三个文件中确实只有一个Log函数定义,报错原因:include语句,Math.cpp与Log.cpp中均进行了#include "Log.h",所以产生了两个Log函数的定义。
解决方法:①将log函数标记为static. 这时该Log函数链接时只发生在内部,即Log函数被include进Math.cpp与Log.cpp时,只会对该文件内部有效。
static void Log(const char* message)
此时build项目成功
②将Log函数标记为inline. inline的意思是将函数本身拿过来,而不是调用
inline void Log(const char* message)
此时build项目成功
③将定义移动至一个翻译单元中(报错原因是这个Log函数被包含在两个翻译单元中),可以把Log函数的定义移动至第三个翻译单元,或者把Log函数定义放到现有的一个翻译单元中。(将其放入Log.cpp,Log.h函数中仅留声明)
//Log.cpp
#include <iostream> //include后,error消失
#include "Log.h"
void InitLog()
{
Log("Initialized Log");
}
void Log(const char* message)
{
std::cout << message << std::endl;
}
//Log.h
#pragma once
void Log(const char* message);
//Math.cpp
#include <iostream>
#include "Log.h"
static int Multiply(int a, int b)
{
Log("Multiply");
return a * b;
头文件中不放函数的定义,仅包含声明就可以。只声明,不定义。
07
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)