1、Run-Time Library
Run-Time Library是编译器提供的标准库,提供一些基本的库函数和系统调用。
我们一般使用的Run-Time Library是C Run-Time Libraries。当然也有Standard C++ libraries。
C Run-Time Libraries实现ANSI C的标准库。VC安装目录的CRT目录有C Run-Time库的大部分源代码。
C Run-Time Libraries有静态库版本,也有动态链接库版本;有单线程版本,也有多线程版本;还有调试和非调试版本。
可以在 "project "- "settings "- "C/C++ "- "Code Generation "中选择Run-Time Library的版本。
动态链接库版本:
/MD Multithreaded DLL 使用导入库MSVCRT.LIB
/MDd Debug Multithreaded DLL 使用导入库MSVCRTD.LIB
静态库版本:
/ML Single-Threaded 使用静态库LIBC.LIB
/MLd Debug Single-Threaded 使用静态库LIBCD.LIB
/MT Multithreaded 使用静态库LIBCMT.LIB
/MTd Debug Multithreaded 使用静态库LIBCMTD.LIB
C Run-Time Library的标准io部分与操作系统的关系很密切,在Windows上,CRT的io部分代码只是一个包装,底层要用到操作系统内核 kernel32.dll中的函数,在编译时使用导入库kernel32.lib。这也就是为什么在嵌入式环境中,我们一般不能直接使用C标准库。
在Linux环境当然也有C标准库,例如:
ld -o output /lib/crt0.o hello.o -lc
参数 "-lc "就是在引用C标准库libc.a。猜一猜 "-lm "引用哪个库文件?
2、常见的编译参数
VC建立项目时总会定义 "Win32 "。控制台程序会定义 "_CONSOLE ",否则会定义 "_WINDOWS "。Debug版定义 "_DEBUG ",Release版定义 "NDEBUG "
与MFC DLL有关的编译常数包括:
_WINDLL 表示要做一个用到MFC的DLL
_USRDLL 表示做一个用户DLL(相对MFC扩展DLL而言)
_AFXDLL 表示使用MFC动态链接库
_AFXEXT 表示要做一个MFC扩展DLL
所以:
Regular, statically linked to MFC _WINDLL,_USRDLL
Regular, using the shared MFC DLL _WINDLL,_USRDLL,_AFXDLL
Extension DLL _WINDLL,_AFXDLL,_AFXEXT
CL.EXE编译所有源文件,LINK.EXE链接EXE和DLL,LIB.EXE产生静态库。
3、subsystem和可执行文件的启动
LINK的时候需要指定/subsystem,这个链接选项告诉Windows如何运行可执行文件。
控制台程序是/subsystem: "console "
其它程序一般都是/subsystem: "windows "
将 subsystem 选成 "console "后,Windows在进入可执行文件的代码前(如mainCRTStartup),就会产生一个控制台窗口。
如果选择 "windows ",操作系统就不产生console窗口,该类型应用程序的窗口由用户自己创建。
可执行文件都有一个Entry Point,LINK时可以用/entry指定。缺省情况下,如果subsystem是“console”,Entry Point是 mainCRTStartup(ANSI)或wmainCRTStartuup(UNICODE),即:
/subsystem: "console " /entry: "mainCRTStartup " (ANSI)
/subsystem: "console " /entry: "wmainCRTStartuup " (UNICODE)
mainCRTStartup 或 wmainCRTStartuup 会调用main或wmain。
值得一提的是,在进入应用程序的Entry Point前,Windows的装载器已经做过C变量的初始化,有初值的全局变量拥有了它们的初值,没有初值的变量被设为0。
如果subsystem是“windows”,Entry Point是WinMainCRTStartup(ANSI)或wWinMainCRTStartup(UINCODE),即:
/subsystem: "windows " /entry: "WinMainCRTStartup " (ANSI)
/sbusystem: "windows " /entry: "wWinMainCRTStartup " (UINCODE)
WinMainCRTStartup 或 wWinMainCRTStartup 会调用 WinMain 或 wWinMain。
如果使用MFC框架,WinMain也会被埋藏在MFC库中(APPMODUL.CPP):
extern "C " int WINAPI
_tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPTSTR lpCmdLine, int nCmdShow)
{
// call shared/exported WinMain
return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
}
"_t "是一个宏,对于ANSI版本, "_tWinMain "就是 "WinMain ";对于UINCODE版本, "_tWinMain "就是 "wWinMain "。
全局C++对象的构造函数是在什么地方调用的?答案是在进入应用程序的Entry Point后,在调用main函数前的初始化操作中。所以MFC的theApp的构造函数是在_tWinMain之前调用的。
4、不显示Console窗口的Console程序
在默认情况下/subsystem 和/entry开关是匹配的,也就是:
"console "对应 "mainCRTStartup "或者 "wmainCRTStartup "
"windows "对应 "WinMain "或者 "wWinMain "
我们可以通过手动修改的方法使他们不匹配。例如:
#include "windows.h "
#pragma comment( linker, "/subsystem:/ "windows/ " /entry:/ "mainCRTStartup/ " " ) // 设置入口地址
void main(void)
{
MessageBox(NULL, "hello ", "Notice ", MB_OK);
}
这个Console程序就不会显示Console窗口。如果选/MLd的话,这个程序只需要链接LIBCD.LIB user32.lib kernel32.lib。
5、VC中缺省库冲突的解决
VC的编译器在编译程序时有两个习惯:
a、在从头开始编译时,将源文件名按字母排序后,依次处理;
b、一边编译一边决定需要哪些缺省库。
它的这些习惯有时会造成奇怪的编译错误,例如项目中有两个文件:
charutil.c
gbnni.cpp
其中gbnni.cpp用到了MFC库。
它老兄当然是先处理charutil.c,然后觉得需要link一个C Runtime库,根据项目设置选择了LIBCMTD.lib。
然后又处理gbnni.cpp,因为要用MFC,又决定要link nafxcwd.lib。
最后link的时候,就会出现以下冲突:
nafxcwd.lib(afxmem.obj) : error LNK2005: "void __cdecl operator delete(void *) " (??3@YAXPAX@Z) already defined in LIBCMTD.lib(dbgdel.obj)
其实,必须先链接nafxcwd.lib,再链接LIBCMTD.lib,所以一个更简单的方法是在settings-link-input中设置nafxcwd.libLIBCMTD.lib
即指定库的顺序。
解决这类问题有两个办法。
a、让项目的第一个文件包含MFC的头文件,这样编译器就不会想到找C Runtime库。这样就要把c文件改成cpp了。
b、将需要link C Runtime库的文件的名字改大一些,让它排在后面。
使用IDE当然很方便,但既然使用了别人写的工具,有时就不得不琢磨、迁就它的习性。
使用dll,采用显式声明时,可以用如下三种方式添加lib:
a.可以到“project”菜单下选择“Add To Project”,“Files”,然后把lib文件加进来。
b.也可以设置工程选项:到“project”菜单下选择“Settings”,然后选择“link”选项卡,在其中的Object/library Modules中填入“yourLib.lib”
c.还有一种方法,直接在CMyDlg.cpp文件开始处添加一句预编译指令:
#pragma comment(lib,” yourLib.lib”)