Author:lostName
Time:2010-4-16
Version:v1.1
Run:VC6.0与VS2008
相信很多人都用过VC6.0与Visual Studio系列产品。
也有很多牛B人士用它们创造过很人的神话,铸就许多美丽的传说。
那你们知道为何你能用它创始出那么多的奇迹,这是你一个人的成功?
你是否关心过你是如何能动态的链接它给你提供的内库,它是如何控制你的IO流的?
为何C++的语法是这么规定的,它又是如何实现这些语法的呢?
本文和接下来的文章很肤浅的介绍了这些东西,让基本上还在一个.cpp和.h文件中写出超然代码的人也能够了看到一些往往忽略的东西。
首先我们来明白一个工程从创建到可执行文件的过程:
创建工程——编写代码——编译——链接——可执行文件。
下一篇日记中会重点介绍VC的编译与链接的过程,此文重点在讲项目、dll与lib。
我相信任何人肯定能从VC6.0的目录下找到两个文件夹lib与include。而include内的都是大家熟悉的头文件(如iostream.h、conio.h)等文件。这些头文大家都是能够查看与打开的,就好比你写的头文件一样,里面有很多的声明。而且我敢肯定的是大家肯定找不到的是.cpp文件。你也许会好奇光有头文件里这些类这些声明,而没有具体的实现的话(c++一般都是在.h中写类的一些接口,而实现的部分是在.cpp文件中),我们是如何来用它的呢?
这就和你的编译器有很大的关系了。不同的编译器处理的方式不通。就拿VC6.0来说:
假设你有3个.cpp文件(a.cpp、b.cpp、c.pp)。在编译器预处理的时候它会把头文件(.h)的内容都会插入到你的对应的.cpp文件中。如a.cpp文件中有#include <iostream>、#include <a.h>。则预处理的时候会把a.h文件的内容都复制到了a.cpp文件中,当然还有include文件夹下的iostream.h内容,当然iostream.h文件中的<ios.h>,<streamb.h>,<istream.h>,<ostream.h>中的内容也会通过递归的方式加入进来( 当然不是都不是整个.h文件的内容都一定要加入进去的,预处理做的事情不只有这么简单,如想了解可以去查资料,这里只是不必增加复杂度,你暂时可以理解为都加入进去了)。
然后每个.cpp文件其实可以理解为一个编译的单元,可以分别编译的。(这样的做法,很明显可以提高速度,如果你每改一个文件,所以文件都要重新都编译下的话,所耗的时间是相当的惊人的,所以c++把每个编译单元都独立出来编译成obj文件,然后在通过链接成为一个可执行文件。)。通过编译每个cpp文件它会产生一个.obj文件。如上面的三个文件它会产生a.obj,b.obj,c.obj文件(还有一些其他文件,但你不需要了解,你需要记住只是这里说的是VC6.0的编译器)。这里面都是2进制的代码,每个obj文件它所含的内容除了编译后的产生的数据和代码外,它还必须含有3个表:未解决符号表、导出符号表、地址重定向表。
先别着急了解这3个表的含义。我们来通过程序了解:
假设有:
A.cpp:
extern int m_a; //声明变量a
void set()
{
m_a = 6;
}
B.cpp:
int m_a = 6; //全局变量a
void add()
{
m_a++;
};
编译这两个文件产生了A.obj、B.obj文件。可这些文件是分别编译的,我们怎么知道A.cpp中的值的大小呢?这就是表的作用了!
.obj内除了自己数据外,还有的导入符号表和未解决符号表就是告诉编译器我能提供什么东西,我还需要什么东西。地址重定向表是解决地址冲突的,这里不做深究。如:
A.obj的导出符号表中有m_a、set()。未解决表中没有。
B.obj的导出符号表中有add()。未解决表中有m_a;
这样以后再链接的时候,编译器去找B.obj需要的m_a的时候找到了唯一的A.obj中导出的m_a。(下一章的内部连接和外部连接会更深入的了解)。
这样然后整合到一个.exe(含main方法的那个cpp文件)文件中成为一个可执行文件。
好了现在说完这些,你就会更加清楚的去了解lib与dll内。
lib分为静态的链接库和动态的链接库,这些也都是2进制代码。
(1)我们不是在VC的lib文件夹下看到了很多的lib文件吗?这些是静态的链接库,你其实可以把它理解为一个项目(项文件含有的obj文件的总和,如为完成某个功能,一般不是只需要一个cpp文件就能完成的,而每个cpp在编译后都会产生obj)的obj文件(为方便理解才这么所,当然不可能这么简单)。当然这些就是Microsoft的VC6.0的源文件发布后能提供的东西。所以我们自己编写的代码中的头文件需要的东西,可以对应的lib找到对应的入口。如iostream.h中的iostream(const iostream&)函数需要的东西可以从lib中找到入口,如果某个对应lib里的导出符号表提供了iostream(const iostream&)函数的入口,我们就调用这lib中的iostream(const iostream&)具体实现(lib里的2进制代码)。
(2)而使用动态链接中的lib,不是obj文件的集合,即里面不会有实际的实现,它只是提供动态链接到DLL(后面有说到它)所需要的信息,这种lib可以在编译一个DLL工程时由编译器生成。
所有你写的代码是结合了前人无数的研究与开发的产品,集合了几代人的成就。你好比是用到了他们所写的源码一样,只是你看不到它的源码(.cpp文件)而已。按照我家GY的话来说就是一代一代哟!
(3)dll是动态链接库。区分动态的链接库与静态的链接库很简单,就是静态的链接库是在程序链接的时候就已经链接到exe文件(可执行文件)内了!而动态的链接库是你的exe(可执行文件)运行的时候需要的链接库。也就是说如果你用到了dll的话,那你光有个exe文件没用,想要运行的话必须还要有动态链接库的存在,如果要完成源代码的编译,只需要lib;如果要使动态链接的程序运行起来,只需要dll。
如果有dll文件(这就是VS2008的内库了,好比就是C++提供的那些lib一样,它这么做的目的还是为了节省了内存资源,提高时间,后面有说到),那么lib一般是一些索引信息,记录了dll中函数的入口和位置,dll中是函数的具体内容;如果只有lib文件,那么这个lib文件是静态编译出来的,索引和实现都在其中。使用静态编译的lib文件,在运行程序时不需要再挂动态库,缺点是导致应用程序比较大,而且失去了动态库的灵活性,发布新版本时要发布新的应用程序才行。
动态链接的情况下,有两个文件:一个是LIB文件,一个是DLL文件。LIB包含被DLL导出的函数名称和位置,DLL包含实际的函数和数据,应用程序使用LIB文件链接到DLL文件。在应用程序的可执行文件中,LIB中存放的不是被调用的函数代码,而是 DLL中相应函数代码的地址,从而节省了内存资源。
(4)总之1个静态的lib可以看成是若干个obj的内容(vc6.0安装包的内容)。而动态的lib一般是和dll一起存在的,这时候的LIB中存放的不是被调用的函数代码,而是 DLL中相应函数代码的地址,而DLL是只能在程序运行的时候才能使用的(VS2008的内容)。