0
点赞
收藏
分享

微信扫一扫

不同表现形式下的main函数

老北京的热干面 2022-01-14 阅读 76
c++

 main函数简介

        main函数,又称主函数,作为绝大部分C程序唯一的入口,是要求有返回值的,该返回值返回给(如操作系统)来表明该程序的执行状况。返回0代表程序正常执行成功,返回非0值代表程序异常结束,因此返回值需要是int整型,于是有了int main()的规范。

        程序执行总是从main函数开始,如果有有其他函数,则完成对其他函数的调用后再返回到主函数,最后由main函数结束整个程序。在执行程序时,由系统调用main函数 。main 函数是在程序启动中完成对具有静态存储期的非局部对象的初始化之后被调用的。它是程序在有宿主环境(亦即有操作系统)中所指定的入口点。自立程序(启动加载器,操作系统内核,等等)的入口点则是由实现定义的。

定义方式

int main(void)
int main(int argc, char *argv[]) // char *argv[]可以写成char **argv

        主函数的两个形参形式中的形参,允许从执行环境中传递任意的多字节字符串(它们通常被称为命令行参数),各个指针 argv[1] .. argv[argc-1] 指向每个这些字符串的第一个字符。argv[0] 是指向一个表示用于执行该程序自身的名字的空结尾多字节字符串(或者当执行环境不支持时,为空字符串 "")的开头字符的指针。这些字符串是可以改动的,虽然对它们的改动并不会被传回给执行环境:比如可以用 std::strtok 来使用它们。由 argv 所指向的数组的大小至少为 argc+1,其最后一个元素 argv[argc] 保证为一个空指针。

不同形式下的main函数

        C/C++程序设计语言中,存在main函数的项目,必然会存在项目可执行程序(如*.exe/*.bin、*.dll/*.so等),不同存在形式,则main函数表现形式有所不同。

1、main()函数

        main()是C/C++的标准入口函数名,如Windows平台下经常使用的Qt库以及控制台应用项目开发中,这是我们入门C/C++程序,控制台程序输出"hello world!"必然会接触到的。

2、WinMain()函数

        WinMain是windows API窗体程序的入口函数(如MFC开发库)。(int WINAPI WinMain()) 中 WINAPI是__stdcall宏,在windef.h中定义的。

3、DllMain()函数

        Windows在加载DLL的时候,需要一个入口函数,就如同控制台或DOS程序需要main函数、Win32程序需要WinMain函数一样。根据编写规范,Windows必须查找并执行DLL里的DllMain函数作为加载DLL的依据,它使得DLL得以保留在内存里。这个函数并不属于导出函数,而是DLL的内部函数。这意味着不能直接在应用工程中引用DllMain函数,DllMain是自动被调用的。 

        注:一些例子中,DLL并没有提供DllMain函数,应用工程也能成功引用DLL,这是因为Windows在找不到DllMain的时候,系统会从其它运行库中引入一个不做任何操作的缺省DllMain函数版本,并不意味着DLL可以放弃DllMain函数。 

BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
 switch (ul_reason_for_call)
 {
  case DLL_PROCESS_ATTACH:
   printf("\nprocess attach of dll");
   break;
  case DLL_THREAD_ATTACH:
   printf("\nthread attach of dll");
   break;
  case DLL_THREAD_DETACH:
   printf("\nthread detach of dll");
   break;
  case DLL_PROCESS_DETACH:
   printf("\nprocess detach of dll");
   break;
 }
 return TRUE;
}

DllMain函数在下面几种情况下被调用:

1) DLL被加载(DLL_PROCESS_ATTACH)

         一个程序要调用Dll里的函数,首先要先把DLL文件映射到进程的地址空间。要把一个DLL文件映射到进程的地址空间,有两种方法:静态链接和动态链接的LoadLibrary或者LoadLibraryEx。

         当一个DLL文件被映射到进程的地址空间时,系统调用该DLL的DllMain函数,传递的fdwReason参数为DLL_PROCESS_ATTACH。这种调用只会发生在第一次映射时。如果同一个进程后来为已经映射进来的DLL再次调用LoadLibrary或者LoadLibraryEx,操作系统不会再用DLL_PROCESS_ATTACH调用DLL的DllMain函数,而是增加DLL的使用次数。不同进程用LoadLibrary同一个DLL时,每个进程的第一次映射都会用DLL_PROCESS_ATTACH调用DLL的DllMain函数。

2) DLL被卸载(DLL_PROCESS_DETACH)

         当DLL被从进程的地址空间解除映射时,系统调用了它的DllMain,传递的fdwReason值是DLL_PROCESS_DETACH。当 DLL处理该值时,它应该执行进程相关的清理工作。 DLL被从进程的地址空间解除映射的情况有两种:

   ① F reeLibrary()被调用。(有几个 LoadLibrary,就要有几个FreeLibrary)

   ②  进程结束。 在进程结束前还没有解除DLL的映射,进程结束后会解除DLL映射。(如果进程的终结是因为调用了TerminateProcess,系统就不会用DLL_PROCESS_DETACH来调用DLL的DllMain函数。这就意味着DLL在进程结束前没有机会执行任何清理工作。)

       注意:当用DLL_PROCESS_ATTACH调用DLL的DllMain函数时,如果返回FALSE,说明没有初始化成功,系统仍会用DLL_PROCESS_DETACH调用DLL的DllMain函数。因此, 必须确保没有清理那些没有成功初始化的东西。

3) 单个线程启动(DLL_THREAD_DETACH)

        当进程创建一线程时,系统查看当前映射到进程地址空间中的所有DLL文件映像,并用值DLL_THREAD_ATTACH调用DLL的DllMain函数。 新创建的线程负责执行这次的DLL的DllMain函数,只有当所有的DLL都处理完这一通知后,系统才允许进程开始执行它的线程函数。

        注意:跟DLL_PROCESS_ATTACH的区别,我们在前面说过,第n(n>=2)次以后地把DLL映像文件映射到进程的地址空间时,只增加使用次数,而不用DLL_PROCESS_ATTACH调用DllMain。DLL_THREAD_ATTACH不同,进程中的每次建立线程,都会用值DLL_THREAD_ATTACH调用DllMain函数,哪怕是线程中建立线程也一样。

4) 单个线程终止(DLL_THREAD_DETACH)

        如果线程调用了ExitThread来结束线程(线程函数返回时,系统也会自动调用ExitThread),系统查看当前映射到进程空间中的所有DLL文件映像,并用DLL_THREAD_DETACH来调用DllMain函数,通知所有的DLL去执行线程级的清理工作。

        注意:如果线程的结束是因为系统中的一个线程调用了TerminateThread(),系统就不会用值DLL_THREAD_DETACH来调用所有DLL的DllMain函数。

4、_tmain()函数、 _tWinMain()函数

        _tmain _tWinMain 是Unicode版本函数别名,对应与wmain()和wWinMain()。

//<tchar.h>
#ifdef _UNICODE

#define _tmain wmain
#define _tWinMain wWinMain

#else /* ndef _UNICODE */

#define _tmain main
#define _tWinMain WinMain

#endif

        这样定义是为了自动适应是否定义了UNICODE,其中wmain和wWinMain是支持UNICODE字符的。前缀为"_t"的应用与UNICODE的函数。

举报

相关推荐

0 条评论