C++学习之路(二):include ,编译和链接
前言一. include二.编译三.链接
0x00.link0x01.多个函数link0x02.include 结束
C++学习之路(二):include ,编译和链接 前言本文主要是学习C++的include,编译和链接,借助Visual Studio工具加深了对于include的理解。
一. includeinclude是我们编码中使用比较频繁的指令,它的作用其实是在预处理的时候,将我们编写的头文件内容直接拷贝到目标文件中。是不是感到很惊讶?
下面用代码来证明一下:
end.h
} // 对这里就一个花括号
math.cpp
int add(int a, int b) {
return a + b;
#include "end.h" // 这里的}给替换成了#include "end.h"
main.cpp
#includeint add(int a, int b); int main() { std::cout << add(2, 3) << std::endl; return 0; }
运行后成功打印2和3的结果,并且输出来。
是不是感到很惊讶,为了更加的清除了解到底发生了什么,我们请Visual Studio来帮忙。
点击项目的属性:
在如下图选择"预处理到文件"中选择是,然后点击应用。(注意配置和平台的选择,需要和你运行的时候选择一样的,我直接选择所有配置)
再次编译math.cpp文件,在如下目录中找到math.i文件
math.i的内容如下:
#line 1 "D:\codework\vswork\Project2\Project2\math.cpp"
int add(int a, int b) {
return a + b;
#line 1 "D:\codework\vswork\Project2\Project2\end.h"
}
#line 4 "D:\codework\vswork\Project2\Project2\math.cpp"
从这里可以很清楚的看到,是直接把end.h的内容拷贝过来的。同理,加上#include
C++的编译其实是把每一个.cpp文件都编译成后缀名为.obj的二进制文件(Visual Studio下)。
.obj文件你可以在你项目的平台和配置下找到,我的是在x64Debug目录下(每个人都不一样)。虽然它是个二进制的文件,但是我们可以借助Visual Studio修改,输出汇编指令:
针对main.cpp源文件,会在我的x64Debug目录下找到main.asm文件,打开之后就是一堆的汇编指令文件。
首先看两段代码:
log.cpp
#includevoid log(const char* msg) { std::cout << msg << std::endl; }
main.cpp
// 注意这里没有include// log是函数声明,学习C语言的都知道,使用函数需要声明一下 void log(const char* msg); int main() { log("hello world !"); return 0; }
看到上面的代码你一定会很惊讶,为什么没有include任何东西,main.cpp可以正常运行起来。在main.cpp代码中只是声明了log函数,那么IDE是如何找到的,这里就是用到了链接(link)。
可以做个测试来测试下:
我们将log.cpp中方法名改为"logr",再编译main.cpp,你会发现编译不会报错,那是因为此时还没有链接(link),如果我们再运行的时候,就会发生错误。
error LNK2019: 无法解析的外部符号 "void __cdecl log(char const *)" (?log@@YAXPEBD@Z),函数 main 中引用了该符号
注意error LNK,这里说明这个是一个链接错误。点击运行之后链接器会进行链接,这时候链接器找不到void log(const char* msg);函数就会报link错误。
0x01.多个函数link再创建一个log0.cpp,代码如下:
// 函数签名和log.cpp签名一样
void log(const char* msg) {
}
原来的log.cpp
#includevoid log(const char* msg) { std::cout << msg << std::endl; }
运行之后报如下错误:
error LNK2005: "void __cdecl log(char const *)" (?log@@YAXPEBD@Z) 已经在 log.obj 中定义
这个也是个link错误,原因是将log函数重复定义导致的,链接器在链接的时候不知道链接哪个函数。对于这种情况你可以在其中一个函数前面加上static(后续会对static做一个学习总结),比如在log0.cpp函数前面加上static,不让链接器去链接。或者直接删除其中一个。
0x02.include对于函数main.cpp的函数声明,可以写一个.h的头文件代替函数声明,这样的好处是使用log函数的时候,就不需要每次使用每次都要声明。
log.h
#ifndef __LOG_H__ #define __LOG_H__ void log(const char* msg); #endif
main.cpp
#include "log.h"
// 原来的log函数声明不需要了
int main() {
log("hello world !");
return 0;
}
事实上根据前面的include章节可以知道,这里的main.cpp等价于加函数声明的main.cpp。
结束通过上面的总结,了解了:
- include预处理命令,是在编译之前就处理了,本质是直接将.h文件内容添加到目标文件中。编译是将源代码文件编译成一个个的.obj文件。运行的时候会将编译好的.obj文件进行链接(link)



