在VC中编译、运行程序的小知识点

原地址:http://topic.youkuaiyun.com/t/20050428/22/3974737.html

        最近我抽空研究、整理了一下VC中几个以前比较模糊的问题,写成这篇短文,希望和碰到过类似问题的朋友共享。   如果我的理解有不正确的地方,欢迎大家指正。  
  文章的3、4小节参照了vcforever的专栏(http://blog.youkuaiyun.com/vcforever/archive/2004/12/14/215936.aspx)。其它信息来源于MSDN和自己的摸索。  
   
  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是WinMain(ANSI)或wWinMain(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)  
  其实,如果先link了nafxcwd.lib,就没有必要再link   LIBCMTD.lib,也就不会产生冲突。  
   
  解决这类问题有两个办法。  
  a、让项目的第一个文件包含MFC的头文件,这样编译器就不会想到找C   Runtime库。这样就要把c文件改成cpp了。  
  b、将需要link   C   Runtime库的文件的名字改大一些,让它排在后面。  
  使用IDE当然很方便,但既然使用了别人写的工具,有时就不得不琢磨、迁就它的习性。  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值