• [综合]在VC中编译、运行程序的小知识点


    同样的标题,两篇的内容却稍有不同,我就综合一下了。

    以下内容来自:

    http://blog.163.com/bestfighter_210@126/blog/static/10361887200811206310788/

    http://demon.tw/programming/vc-compile-skill.html

    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标准库。

    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。

    这些入口点函数,在CRT目录都可以看到源代码,例如(为了简洁,我删除了原代码的一些条件编译):

    void mainCRTStartup(void)
    {
            int mainret;
    
            /* Get the full Win32 version */
            _osver = GetVersion();
            _winminor = (_osver >> 8) & 0x00FF ;
            _winmajor = _osver & 0x00FF ;
            _winver = (_winmajor << 8) + _winminor;
            _osver = (_osver >> 16) & 0x00FFFF ;
    
    #ifdef _MT
            if ( !_heap_init(1) )               /* initialize heap */
    #else  /* _MT */
            if ( !_heap_init(0) )               /* initialize heap */
    #endif  /* _MT */
                fast_error_exit(_RT_HEAPINIT);  /* write message and die */
    
    #ifdef _MT
            if( !_mtinit() )                    /* initialize multi-thread */
                fast_error_exit(_RT_THREAD);    /* write message and die */
    #endif  /* _MT */
    
            __try {
                _ioinit();                      /* initialize lowio */
                _acmdln = (char *)GetCommandLineA();        /* get cmd line info */
                _aenvptr = (char *)__crtGetEnvironmentStringsA();        /* get environ info */
                _setargv();
                _setenvp();
                __initenv = _environ;
                mainret = main(__argc, __argv, _environ);
                exit(mainret);
            }
            __except ( _XcptFilter(GetExceptionCode(), GetExceptionInformation()) )
            {
                _exit( GetExceptionCode() );        /* Should never reach here */
            } /* end of try - except */
    }  

    如果使用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);
    
    }

    对于ANSI版本,"_tWinMain"就是"WinMain";对于UINCODE版本,"_tWinMain"就是"wWinMain"。可参见afx.h:

    #ifdef _UNICODE 
    
    #define _tmain wmain
    
    #define _tWinMain wWinMain
    
    #else
    
    #define _tmain main
    
    #define _tWinMain WinMain
    
    #endif

    全局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。

    其实如果不想看到Console窗口,还有一个更直接的方法:那就是直接在EXE文件中将PE文件头的Subsystem从3改成2。在EXE文件中,PE文件头的偏移地址是0x3c,Subsystem是一个WORD,它在PE文件头中的偏移是0x5c。

    5、MFC的库文件

    MFC的库可以静态链接,也可以动态链接。静态库和动态库又有Debug和Release,ANSI和Unicode版本之分。

    静态MFC库主要有:

    ANSI Debug NAFXCWD.LIB

    ANSI Release NAFXCW.LIB

    Unicode Debug UAFXCWD.LIB

    Unicode Release UAFXCW.LIB

    动态链接库主要有;

    ANSI Debug MFCxxD.LIB (core,MFCxxD.DLL),

    MFCOxxD.LIB (OLE,MFCOxxD.DLL),

    MFCDxxD.LIB (database,MFCDxxD.DLL),

    MFCNxxD.LIB (network,MFCNxxD.DLL),

    MFCSxxD.LIB (static)

    ANSI Release MFCxx.LIB (combined,MFCxx.DLL)

    MFCSxx.LIB (static)

    Unicode Debug MFCxxUD.LIB (core,MFCxxUD.DLL),

    MFCOxxUD.LIB (OLE,MFCOxxUD.DLL),

    MFCDxxUD.LIB (database,MFCDxxUD.DLL),

    MFCNxxUD.LIB (network,MFCNxxUD.DLL),

    MFCSxxUD.LIB (static)

    Unicode Release MFCxxU.DLL (combined,MFCxxU.DLL),

    MFCSxxU.LIB (static)

    上面的LIB文件除了MFCSxx(D、U、UD).LIB以外都是导入库。

    MFC动态链接库版本也需要静态链接一些文件,这些文件就放在MFCSxx(D、U、UD).LIB中。例如包含_tWinMain的appmodul.cpp。

    6、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当然很方便,但既然使用了别人写的工具,有时就不得不琢磨、迁就它的习性。

  • 相关阅读:
    Swift开发--Storyboard的使用教程
    HDU 2188-悼念512汶川大地震遇难同胞――选拔志愿者(巴什博奕)
    编译iOS使用的.a库文件
    springMVC3学习(十)--注解式控制器
    oracle RANK() dense_rank()
    sum(),max(),avg(),RATIO_TO_REPORT()--分组统计
    cume_dist(),允许并列名次、复制名次自动空缺,取并列后较大名次,结果如22355778……
    cume_dist(),名次分析——-最大排名/总个数
    rank(),允许并列名次、复制名次自动空缺,结果如12245558……
    ROW_NUMBER(),不允许并列名次、相同值名次不重复,结果如123456……
  • 原文地址:https://www.cnblogs.com/s5689412/p/2087358.html
Copyright © 2020-2023  润新知