标签:
1.Makefile就知道了。先直接用命令行操作,然后用集成的IDE来写代码。
2.对于编译过程,总体上是这样
<1>源代码(*.h,*.cpp/c)经过预编译,编译,生成目标文件(Windows下应该是.obj文件,Linux/unix下是.o文件)
<2>然后通过链接(将各种目标文件.obj(.o) 和 目标文件的集合(动态静态库dll(windows下),so(linux/unix下)))
<3>最终成功可执行文件(Windows下叫exe,Linux/unix下随便以什么结尾了)。
*.obj,*.pch,*.dsp,*.ncb,*.plg 这些,除了obj,其他都是微软集成的编译器做的事情了,微软其实也有个类似makefile的东西,其实你可以不用去关注的。这些C++本身无关。
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
c++程序在编译后,在目标路径下会生成多个文件:
Debug文件夹(*.exe,*.ilk,*.obj,*.pch,*.pdb,*.idb,*,pdb),*.cpp,*.dsp,*.ncb,*.plg
*.exe:是生成的可执行文件
*.ilk:当选定渐增型编译连接时,连接器自动生成ILK文件,记录连接信息
*.obj:是目标文件,源程序编译后的产物
*.pch:全称是PreCompiled Header,就是预先编译好的头文件
*.idb:文件保存的信息,使编译器在重新编译的时候只重编译最新改动过的函数和只对最新类定义改动过的源文件进行重编译,以提高编译速度
*.pdb:全称是Program DataBase,即程序数据库文件,用来记录调试信息
*.dsp:(全称是Developer Studio Project)也是一个配置文件
*.ncb:(全称No Compile Browser)的缩写,其中存放了供ClassView、WizardBar和Component Gallery使用的信息,由VC开发环境自动生成
*.plg:实际上是一个超文本文件,可以用Internet Explorer打开,记录了Build的过程
*.cpp:就是C++源代码文件.
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Part1
这里讲下C++文件的编译过程及其中模板的编译过程;
一:一般的C++应用程序的编译过程。
一般说来,C++应用程序的编译过程分为三个阶段。模板也是一样的。
下面分别描述这几个阶段。
1.include文件的展开。
include文件的展开是一个很简单的过程,只是将include文件包含的代码拷贝到包含该文件的cpp文件(或者其它头文件)中。被展开的cpp文件就成了一个独立的编译单元。在一些文章中我看到将.h文件和.cpp文件一起看作一个编译单元,我觉得这样的理解有问题。至于原因,看看下面的几个注意点就可以了。
1):没有被任何的其它cpp文件或者头文件包含的.h文件将不会被编译。也不会最终成为应用程序的一部分。先看一个简单的例子:
1 #include <iostream>
2 #include "testTemplate.h"
3
4 int main()
5 {
6 // 1:实例化一个类模板。
7 // MyClass<int> myClass;
8
9 // 2:调用类模板的成员函数。
10 // myClass.printValue(2);
11
12 std::cout << "Hello world!" << std::endl;
13 return 0;
14 }
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
最近,有同事向我多次问及C++关于编译链接方面的问题,包括如下:
1:什么样的函数以及变量可以定义在头文件中
2:extern "C"的作用
3:防止重复包含的宏的作用
4:函数之间是怎么链接起来的
我认为,这些问题不难,书上基本上都有,但要是没有真正思考过,就凭死记硬背,也就是只能“嘴上说说”而已,遇到问题还真棘手,所以我觉得有必要说一下。
C/C++的编译链接过程
其实,“编译”这个词大多数时候,我们指的是由一堆.h,.c,.cpp文件生成链接库或者可执行文件的过程。但是拿C/C++来说,其实这是很模糊的,由一堆C/C++文件生成应用程序包括预处理---编译文件---链接(写的比较粗糙,不影响本文论述)。
首先,要明白什么是编译单元,一个编译单元可以认为是一个.c或者.cpp文件,每一个编译单元首先会经过预处理得到一个临时的编译单元,这里称为tmp.cpp,预处理会把.c或者.cpp直接或者间接包含的其它文件(不只局限于.h文件,只要是#include即可)的内容替换进来,并展开宏调用等。
下面首先看一个例子:
a.h
其中#define A_H_的作用是防止重复包含a.h这个头文件,很多人都知道这一点,但是再仔细问,我见过大多数人都说不清楚。
这种宏是为了防止一个编译单元(cpp文件)重复包含同一个头文件。它在预处理阶段起作用,预处理器发现a.cpp内已经定义过A_H_这个宏的话,在a.cpp中再次发现#include "a.h"的时候就不会把a.h的内容替换进a.cpp了。
编译器看到tmp.cpp的时候,会编译成一个obj文件,最后由链接器对这一个对obj文件进行链接,从而得到可执行程序。
编译错误和连接错误
编译错误指的是一个cpp编译单元在编译时发生的错误,这种错误一般都是语法错误,拼写错误,参数不匹配等。
以main.cpp为例(只有一个main函数)
有的人说为什么不需要写一个hello_world.h的头文件,声明hello_world函数,然后再让main.cpp包含hello_world.h呢?这样写自然是标准的做法,不过预处理过后,和我们现在写的一样的,预处理会把hello_world.h的内容替换到main.cpp中。
问题:在链接的时候,main.o怎么知道hello_world函数定义在hello_world.o中呢?
答案:main.o不知道hello_world函数定义在那个obj文件中,每个obj文件都有一个导出符号表,对于这个例子,hello_world.o的导出符号表中有hello_world这个函数,而main.o需要用到这个函数,可以想象就像几个插槽一样。链接器通过扫描obj文件发现这个函数定义在hello_world.o中,然后就可以链接了。
问题:为什么函数不能定义在头文件中?
这个问题是不恰当的,因为用inline和static修饰的函数可以定义在头文件中,而inline修饰的函数必须定义在头文件中。
如果函数定义在头文件中,并且有多个cpp文件都包含了这个头文件的话,那么这些cpp文件生成的obj文件的导出符号表中都有这个头文件中定义的函数,单文件编译的时候是不会出错的,但是链接的时候就会报错。链接器发现了多个函数实体,但却无法确定应该使用哪一个。这是一个链接错误。
inline修饰的函数,通常都不会存在函数实体,即便编译器没有对其内联,那么obj文件也不会导出inline函数,所以链接不会出错。
static修饰的函数,只能由定义它的编译单元调用,也不会导出。如果头文件中顶一个static修饰的函数,就相当于多个obj文件中都顶一个了一个一模一样的函数,大家各用各的,互补干扰。
问题:什么样的变量可以定义在头文件中?
其实变量于函数很类似,由static或const修饰的变量可以定义在头文件中。
static修饰的变量于static修饰的函数一样,道理同上。
const修饰的变量默认是不会进入导出符号表的,相当于每个obj中都定义了一个一模一样的const变量,各用各的。而const可以再用extern修饰,如果用extern const修饰的变量定义在头文件中,那么就会出现链接错误,原因就是“想一想extern是干嘛的”
问题:extern "C"是干嘛的?
如果有人回答“兼容C和C++”,我只能说“这是一个正确答案,但我不知道你是否真的知道”。
首先要知道C不支持重载,C++支持重载,C++为了支持重载,引入了函数重命名的机制,就像下面这样:
而我们写C++程序的时候,通常会引入由c编写的库(gcc编译的c文件),而c不支持重载,自然不会对函数重命名。而我们在C++中调用的地方很可能会重命名,这就造成了调用的地方(C++编译)和定义的地方(C编译)函数名不一致的情况,这也是一种链接错误。
所以我们经常会看到在C++中用extern "C" { #include "some_c.h" }这种代码。这就是告诉c++编译器,some_c.h中的函数要按照c的方式编译,不要重命名,这样在链接的时候就ok了。
reference link:
http://blog.csdn.net/peter_teng/article/details/9252711
http://blog.csdn.net/qq575787460/article/details/18671137
标签:
原文地址:http://blog.csdn.net/u011534057/article/details/51334237