文章目录
- C++学习之路(二):头文件,编译和链接
- 前言
- 一. 头文件
- 二.编译
- 三.链接
- 0x00.Link
- 0x01.多个函数Link
- 0x02.include
- 结束
C++学习之路(二):头文件,编译和链接 前言 本文主要是学习C++的include,编译和链接,借助Visual Studio工具加深了对于include的理解 。
一. 头文件 头文件一般都是以.h为后缀的文件,每个C/C++的通常由头文件和定义文件组成,头文件承载着函数和类,下面来看个头文件的简单使用 。
log.h
void log(const char* msg); main.cpp#include "log.h" // 引入上面的头文件int main() { log("hello world !"); return 0;} 以上就是"include"和头文件最简单的使用 。那么include到底是怎么被引入的呢?事实上,编写好的头文件内容,在预处理的时候是直接被拷贝到目标文件中,关于这一点是不是感到很惊讶?
下面我们用代码来证明一下:
end.h
} // 对这里就一个花括号 math.cppint 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,其实是把iostream.h的内容全部拷贝过来 。二.编译 C++的编译其实是把每一个.cpp文件都编译成后缀名为.obj的二进制文件(Visual Studio下) 。
.obj文件你可以在你项目的平台和配置下找到,我的是在
\x64\Debug目录下(每个人都不一样) 。虽然它是个二进制的文件,但是我们可以借助Visual Studio修改,输出汇编指令:针对main.cpp源文件,会在我的
\x64\Debug目录下找到main.asm文件,打开之后就是一堆的汇编指令文件 。三.链接 0x00.Link 首先看两段代码:
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 。结束 【二 C++学习之路:头文件 ,编译和链接】通过上面的总结,了解了:
- include预处理命令,是在编译之前就处理了,本质是直接将.h文件内容添加到目标文件中 。
- 编译是将源代码文件编译成一个个的.obj文件 。
- 运行的时候会将编译好的.obj文件进行链接(link)
- 春季老年人吃什么养肝?土豆、米饭换着吃
- 三八妇女节节日祝福分享 三八妇女节节日语录
- 老人谨慎!选好你的“第三只脚”
- 校方进行了深刻的反思 青岛一大学生坠亡校方整改校规
- 脸皮厚的人长寿!有这特征的老人最长寿
- 长寿秘诀:记住这10大妙招 100%增寿
- 春季老年人心血管病高发 3条保命要诀
- 眼睛花不花要看四十八 老年人怎样延缓老花眼
- 香槟然能防治老年痴呆症? 一天三杯它人到90不痴呆
- 老人手抖的原因 为什么老人手会抖
