程序编译过程,头文件,库文件

三种关键文件

  • 头文件(.h): 工程– 属性—配置属性–c/c++—常规—附加包含目录:加上头文件存放目录
  • 静态库(.lib): 1:工程—属性—配置属性—链接器—常规—附加库目录:加上lib文件存放目录
    2:工程—属性—配置属性—链接器—输入—附加依赖项 :加上lib文件名

  • 动态库(.dll): 把引用的dll放到工程的可执行文件所在的目录下

预处理:

包含头文件的 #include 和定义宏的 #define,同时还有规范性的头文件保护宏,在这个环节出现最多的问题是找不到头文件,然后编译器停止工作。

这里我们首先要区分 3 种头文件,
- 第一种是标准库的头文件,
- 第二种是代码中引用到的第三方库的头文件,
- 第三种是自己代码编写的头文件。

编译器在工作时是有一个头文件目录列表的,根据目录列表去寻找头文件,第一个目录便是当前代码的所在的目录,其次是编译器自行定义的目录(一般是标准库头文件所在的目录),最后是我们自己在编译时加上的目录列表(附加包含目录),可以有多个,包括需要引用的第三方库的头文件目录和自己代码的头文件目录(可能你自己写的头文件跟源文件不在一个目录下)

编译与汇编:

链接

归根结底主要是两种,第一是链接时找不到符号,第二是链接时找到了多个符号。一个符号可以指代一块内存或者一段代码。代码中与符号相关的几处地方如下。
- 变量的声明,告诉编译器有这么一个变量指代一块内存。
- 变量的定义,告诉编译器需要为这个变量分配一块内存。
- 函数的声明,告诉编译器有这么一段代码可以使用,输入输出规范如何,应该怎么调用。
- 函数的定义,告诉编译器这段代码的逻辑实现。
- 引用变量或函数,代码中使用某个变量或者调用某个函数。

编译器会给每个变量和每个函数分配一个符号,这样做的好处是方便符号的重用(函数的重用),也利于项目代码的模块化,多个目标文件的链接。由于每个源文件代码都是独立编译的,并生成目标文件,编译器在处理这个源文件时,最后会在目标文件中指出它所需要的符号和它能够提供的符号,这样,链接器在链接一堆目标文件时(库所提供的目标文件和自己代码的目标文件)就能够为每个待确定的符号找到对应的符号,从而成功生成可执行文件或者库文件

找不到符号:undefined reference to xxx
这个问题估计大部分同学在自己编译代码的时候都碰到过,绝大多数情况下都是编译时配置出错,没有告诉链接器应该去链接某个文件,而导致找不到符号。然而在有时候已经完全配置好了,还是会出现这种情况,即我知道这个库的符号全在这个目标文件中或者这个静态库或者这个动态库中,但是编译器还是报错说找不到符号。这种情况带出了一些更深层次的问题。

例如:C和 C++的不和谐:C 中是不允许函数重名的,但是 C++ 中可以通过不同的输入参数类型和类型次序来重载同名函数,暂且不论重载带来的好处和坑,C++ 能这么做是因为 C++ 编译器会重写每个函数最后生成的符号,上面两个函数在编译完后会生成不同的符号,这样一来,对链接器来说其实函数名相同已经没有什么意义了。 gcc 编译出来的符号已经跟函数名不一样了,符号包含了更多的信息,比如符号的类型(这个符号是个函数),和函数对应参数的类型,相当复杂。相对来说 C 代码编译出来的符号是和函数名是一致的,同时符号中也不区分变量和函数。以上的代码只是很简单的函数,如果加上命名空间,类函数等,编译器产生的符号会更加复杂,更加吓人,这也是为什么我们看到的链接出错中会有一长串的字符,因为 C++ 中的符号异常复杂,包含的信息太多。

所有我们可以看到好多 C 语言库的头文件里会写下面这种代码。

#ifdef __cplusplus
extern "C" {
#endif

......
......

#ifdef __cplusplus
}
#endif

通过这种方式告诉编译器,这个头文件的所有符号请按照 C 语言的规则进行生成,不要采用 C++ 那套符号重写机制。如果不采取这种措施,就会导致原本在库中是 foo 的符号被改写成 _Z3fooi 类似的形式而造成链接失败。

C++ 编译器中符号的兼容性:

多数情况下,我们使用的第三方库都是库的提供者事先编译好的,这就带来了一个很大的隐患。同一个函数在库中的符号和我们编译器要寻找的符号可能不一致,这个问题在 MSVC 上尤为突出。除去动态库静态库的差异,针对相同编译器的不同版本,同一个函数可能生成的符号会不一样,这是最最坑爹的地方。看看 OpenCV 里 VC10,VC11,VC12 的各个目录就知道这个差异是非常大的。


原文:http://blog.luoyetx.com/2015/12/compiler/

编译头文件今天在改一个很大的程序,慢慢看,慢慢改。突然发现一个.c文件,里面什么也没有,就几个头文件,我一看,我靠,这不是把简单的问题搞复杂了吗,随手删掉那个c文件。结果不能编译了,我靠:fatal error C1083: Cannot open precompiled header file: \'Debug/v13_3.pch\':No such file or directory怎么rebuild all都不行。上网查了一下,才搞懂了:----------------总结------如果工程很大,头文件很多,而有几个头文件又是经常要用的,那么1。把这些头文件全部写到一个头文件里面去,比如写到preh.h2。写一个preh.c,里面只一句话:#include "preh.h"3。对于preh.c,在project setting里面设置creat precompiled headers,对于其他.c文件,设置use precompiled header file//哈哈我试了一下,效果很明显,不用precompiled header,编译一次我可以去上个厕所,用precompiled header,编译的时候,我可以站起来伸个懒腰,活动活动就差不多啦---------转载的文章----------预编译头的概念:所谓的预编译头就是把一个工程中的那一部分代码,预先编译好放在一个文件里(通常是以.pch为扩展名的),这个文件就称为预编译头文件这些预先编译好的代码可以是任何的C/C++代码--------甚至是inline的函数,但是必须是稳定的,在工程开发的过程中不被经常改变。如果这些代码被修改,则需要重新编译生成预编译头文件。注意生成预编译头文件是很耗时间的。同时你得注意预编译头文件通常很大,通常有6-7M大。注意及时清理那些没有用的预编译头文件。也许你问:现在的编译器都有Time stamp的功能,编译器编译整个工程的时候,它只编译那些经过修改的文件,而不编译那些从上次编译过,到现在没有被修改过的文件。那么为什么还要预编译头文件呢?答案在这里,我们知道编译器是以文件为单位编译的,一个文件经过修改后,重新编译整个文件,当然在这个文件里包含的所有头文件中的东西(.eg Macro, Preprocesser )都要重新处理一遍。VC的预编译头文件保存的正是这部分信息。以避免每次都要重新处理这些头文件。预编译头的作用:根据上文介绍,预编译头文件的作用当然就是提高便宜速度了,有了它你没有必要每次都编译那些不需要经常改变的代码。编译性能当然就提高了。预编译头的使用:要使用预编译头,我们必须指定一个头文件,这个头文件包含我们不经常改变的代码和其他的头文件,然后我们用这个头文件来生成一个预编译头文件(.pch文件)想必大家都知道 StdAfx.h这个文件。很多人都认为这是VC提供的一个“系统级别”的,编译器带的一个头文件。其实不是的,这个文件可以是任何名字的。我们来考察一个典型的由AppWizard生成的MFC Dialog Based 程序的预编译头文件。(因为AppWizard为我们指定好如何使用预编译头文件,默认的是StdAfx.h,这是VC起的名字)。我们发现这个头文件里包含了以下的头文件:#include // MFC core and standard components#include // MFC extensions#include // MFC Automation classes#include // MFC support for Internet Explorer 4Common Controls#include <br
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值