C++学习--入口函数

在学习第一个C++程序的时候发现控制台程序的入口函数是int _tmain而不是main,查了资料才发现_tmain()是为了支持unicode所使用的main一个别名,宏定义在<stdafx.h>,有这么两行

#include <stdio.h>
#include <tchar.h>

可以在头文件<tchar.h>里找到_tmain的宏定义

#define _tmain      wmain

所以,经过预编译以后, _tmain就变成main了.

#define _tWinMain   wWinMain\

参考一下文摘:

关于不同的程序入口,main(), _tmain(),WinMain(),wmain()?

(2012-07-01 23:15:16)

转载

标签:

杂谈

分类: C编程

http://topic.csdn.net/t/20010930/15/308713.html

答:main()是WINDOWS的控制台程序(32BIT)入口或DOS程序(16BIT)入口,

WinMain()是WINDOWS的GUI程序入口,
wmain()是UNICODE版本的main(),
_tmain()是个宏,如果是UNICODE则他是wmain()否则他是main()

呵呵,看下面这篇博文,发现程序入口点并没有我原先想象的那么简单

可执行程序的入口点在那里?
http://blog.163.com/[email protected]/blog/static/4243883720091053548157/

今天终于有时间来研究一下一个很大很大的工程编译成一个exe和若干dll后,程序是如果执行它的第一条指令的?操作系统以什么规则来找到应该执行的第一条指令(或说如何找到第一个入口函数的)?

我们以前写windows程序时,都是先写个main()函数,然后再写自己的逻辑,然后编译,然后点击exe就能运行我们的程序了;如果我们用VS2005工具生成一个非空工程,工程会为我们提供一个int
_tmain(int argc, _TCHAR*
argv[])或WinMain()函数的入口,然后我们在里面添加程序等等。我们上学时这是这样做了,但是很多人这时理所当然的,很少人会去问为什么会这样?

我读了MSDN里面的讲解才弄出点眉目了,其实我们以前所写的以main()函数开始的程序都是一个半成品
剩下的也是与系统息息相关的工作由编译器帮我们代劳了。怎么回事呢?编译器是如何帮我们代劳的呢?那么程序被系统加载时,准确的说是被系统中的加载器加载
时又是如何知道编译器在我们写的程序上做了手脚呢?难道编译器和加载器之间有什么协定吗?这一些列的问题,做为刚入行的你是否在心里问过自己没有!?

我们以前写的程序在编译器编译成为一个模块(可能是obj文件或其他形式),然后链接器会将一些所需要的库文件和刚才编译器生成的文件进行链接,最终生成
一个exe文件,在所链接的库文件中就包含CRT运行时库,这就是我们今天谈论的主角。在运行时库里面有好一个已经定义如下的函数函数:

(1)mainCRTStartup(或
wmainCRTStartup)       //使用
/SUBSYSTEM:CONSOLE 的应用程序

(2)WinMainCRTStartup(或 wWinMainCRTStartup) //使用
/SUBSYSTEM:WINDOWS 的应用程序

(3)_DllMainCRTStartup                        
//调用 DllMain(如果存在),DllMain 必须用 __stdcall 来定义

其中w开头的函数时unicode版本的,分割符‘//’后面的是入口点函数匹配的subsystem(msdn中查看subsystem)属性设置。

如果未指定 /DLL 或 /SUBSYSTEM (也就是subsystem选项)选项,则链接器将根据是否定义了 main 或
WinMain 来选择子系统和入口点。 函数 main、WinMain 和 DllMain 是三种用户定义的入口点形式。

在默认情况下,如果你的程序中使用的是main()或_main()函数,这链接器会将你的使用(1)中的函数连接到你的exe中;如果你的函数是以
WinWain()函数开始的则连接器使用(2)中的函数连接进exe中;如果我们写的是DLL程序这连接进DLL的是(3)中的函数。

用我们写的程序最终生成的exe执行时,一开始执行的就是上面的函数之一,而不是我们程序所写的main或WinMain等。那么链接器为什么要这样做呢?这就是因为我们写的程序必须要使用到各种各样的运行时库函数才能正常工作,所有在执行我们自己写程序之前必须要先准备好所需要的一切库,噢,明白了吧,之所以要链接它们是因为他们肩负着很重要的使命,就是初始化好运行时库,准备在我们的程序执行时调用。

那么这些函数具体做了什么呢?通过MSDN我们可以知道---它们会去进一步调用其他函数,使得C/C++
运行时库代码在静态非局部变量上调用构造函数和析构函数。

先摘录一段msdn的解释如下:

When you link your image, you either
explicitly or implicitly specify an entry point that the operating
system will call into after loading the image. For a DLL, the
default entry point is DllMainCRTStartup. For an
EXE, it is WinMainCRTStartup. You can override the default with the /ENTRY linker
option. The CRT provides an implementation for
DllMainCRTStartup,
WinMainCRTStartup, and
wWinMainCRTStartup (the Unicode entry point for an
EXE). These CRT-provided entry points call
constructors on global objects and initialize other data
structures that are used by some CRT
functions. This startup code adds about 25K to your image if
it is linked statically. If it is linked dynamically, most of the
code is in the DLL, so your image size stays small.

大家看到了吧,上面我用红色标志了吗,我们可以使用链接器的链接选择来设置我们的函数入口点,但是最好不要这样做,原因就是我用蓝色标志的地方,如果我们重新设置入口点函数,则必须要在入口点函数中自己写上有关的初始化工作,这样岂不麻烦,所有我们最好用默认的入口点函数。

修改入口点方法:proerties->Linker->Advanced->EntryPoint

如果函数与链接器的SubSystem的属性要一致的:

proerties->Linker->System->SubSystem

如果未指定 /DLL 或 /SUBSYSTEM 选项,则链接器将根据是否定义了 main 或 WinMain
来选择子系统和入口点。 函数 main、WinMain 和 DllMain 是三种用户定义的入口点形式。

通过上面的分析就知道,在微软系统中原来操作系统中的加载器与链接器之间是有协议的,要不然在加载运行程序时不可能成功的,比如你将windows程序放到apple系统上运行,就会无法运行,因为apple的加载程序根本不知道加载windows的exe的协议。

给出一篇博文,该博文讲的比较好:

http://tb.blog.csdn.net/TrackBack.aspx?PostId=455591

设有一个Win32下的可执行文件MyApp.exe,这是一个Win32应用程序,符合标准的PE格式。MyApp.exe的主要执行代码都集中在其源
文件MyApp.cpp中,该文件第一个被执行的函数是WinMain。初学者会认为程序就是首先从这个WinMain函数开始执行,其实不然。

在WinMain函数被执行之前,有一系列复杂的加载动作,还要执行一大段启动代码。运行程序MyApp.exe时,操作系统的加载程序首先为进程分配一
个4GB的虚拟地址空间,然后把程序MyApp.exe所占用的磁盘空间作为虚拟内存映射到这个4GB的虚拟地址空间中。一般情况下,会映射到虚拟地址空
间中0X00400000的位置。加载一个应用程序的时间比一般人所设想的要少,因为加载一个PE文件并不是把这个文件整个一次性的从磁盘读到内存中,而
是简单的做一个内存映射,映射一个大文件和映射一个小文件所花费的时间相差无几。当然,真正执行文件中的代码时,操作系统还是要把存在于磁盘上的虚拟内存
中的代码交换到物理内存(RAM)中。但是,这种交换也不是把整个文件所占用的虚拟地址空间一次性的全部从磁盘交换到物理内存中,操作系统会根据需要和内
存占用情况交换一页或多页。当然,这种交换是双向的,即存在于物理内存中的一部分当前没有被使用的页也可能被交换到磁盘中。

接着,系统在内核中创建进程对象和主线程对象以及其它内容。

然后操作系统的加载程序搜索PE文件中的引入表,加载所有应用程序所使用的动态链接库。对动态链接库的加载与对应用程序的加载完全类似。

再接着,操作系统执行PE文件首部所指定地址处的代码,开始应用程序主线程的执行。首先被执行的代码并不是MyApp中的WinMain函数,而是被称为
C
Runtime startup
code的WinMainCRTStartup函数,该函数是连接时由连接程序附加到文件MyApp.exe中的。该函数得到新进程的全部命令行指针和环
境变量的指针,完成一些C运行时全局变量以及C运行时内存分配函数的初始化工作。如果使用C++编程,还要执行全局类对象的构造函数。最
后,WinMainCRTStartup函数调用WinMain函数。

WinMainCRTStartup函数传给WinMain函数的4个参数分别为:hInstance、hPrevInstance、lpCmdline、nCmdShow。

hInstance:该进程所对应的应用程序当前实例的句柄。WinMainCRTStartup函数通过调用GetStartupInfo函数获得该参
数的值。该参数实际上是应用程序被加载到进程虚拟地址空间的地址,通常情况下,对于大多数进程,该参数总是0X00400000。

hPrevInstance:应用程序前一实例的句柄。由于Win32应用程序的每一个实例总是运行在自己的独立的进程地址空间中,因此,对于Win32
应用程序,WinMainCRTStartup函数传给该参数的值总是NULL。如果应用程序希望知道是否有另一个实例在运行,可以通过线程同步技术,创
建一个具有唯一名称的互斥量,通过检测这个互斥量是否存在可以知道是否有另一个实例在运行。

lpCmdline:命令行参数的指针。该指针指向一个以0结尾的字符串,该字符串不包括应用程序名。

nCmdShow:指定如何显示应用程序窗口。如果该程序通过在资源管理器中双击图标运行,WinMainCRTStartup函数传给该参数的值为
SW_SHOWNORMAL。如果通过在另一个应用程序中调用CreatProcess函数运行,该参数由CreatProcess函数的参数
lpStartupInfo(STARTUPINFO.wShowWindow)指定。

时间: 2025-01-02 05:34:59

C++学习--入口函数的相关文章

学习04-jq(jq的使用,jq的入口函数,jq与DOM的转换,基本选择器,层级选择器,设置样式,筛选选择器,筛选方法)

jq的使用 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <scrip

VS2010中wmain入口函数中使用wprintf输出中文乱码问题

生活中的单例 中国(China),位于东亚,是一个以华夏文明为主体.中华文化为基础,以汉族为主要民族的统一多民族国家,通用汉语.中国疆域内的各个民族统称为中华民族,龙是中华民族的象征.古老的中国凭借自身的发展依旧美丽的屹立于东方民族之林,闪耀着她动人的光彩,世界上只有一个中国,任何部分都是祖国不可分割的一部分,今天我们的设计模式就从伟大的祖国开始说起---单例模式. 详解单例模式 单例模式是什么?跟我们的祖国有着怎样的关系呢?首先我们来看一下单例,从"单例"字面意思上理解为-一个类只有

jQuery - 01. jQuery特点、如何使用jQuery、jQuery入口函数、jQuery和DOM对象的区别、jQuery选择器、

this指的是原生js的DOM对象 .css(""):只写一个值是取值,写俩值是赋值 window.onload   ===   $(document).ready(); $(""):获取元素   标签名..类名.#id jQuery特点 链式编程 jq.shou(3000).html(内容) 相当于 jq.shou(3000) jq.gtml(内容) 隐式迭代 隐式实用for循环.迭代 如何使用jQuery 引包 一定要在使用之前 <script src =

JavaScript笔记(三):JS也有入口函数Main

在C和Java中,都有一个程序的入口函数或方法,即main函数或main方法.而在JavaScript中,程序是从JS源文件的头部开始运行的.但是某种意义上,我们仍然可以虚构出一个main函数来作为程序的起点,这样一来不仅可以跟其他语言统一了,而且说不定你会对JS有更深的理解. 1. 实际的入口 当把一个JavaScript文件交给JS引擎执行时,JS引擎就是从上到下逐条执行每条语句的,直到执行完所有代码. 2. 作用域链.全局作用域和全局对象 我们知道,JS中的每个函数在执行时都会产生一个新的

Java中static方法、程序入口函数main方法的继承问题

对于继承问题,说明如下: 1.若父类中属性或方法使用private修饰,则不能在子类中访问:对于使用protected.default.public修饰的方法或属性,都是可以在子类中访问的. 2.在1中所述的可被子类访问的前提下,对于static修饰的方法或属性,表示该属性或方法是独立于具体的对象事例:它们也都是可以被子类继承的. 3.对于父类中的入口函数(main方法)也是可以被子类继承的.ps:具体的可以使用命令行运行java程序进行验证.

对比JavaScript的入口函数和jQuery的入口函数

JavaScript的入口函数要等到页面中所有的资源(包括图片.文件)加载完成才开始执行. jQuery的入口函数只会等待文档数加载完成就开始执行,并不会等待图片.文件的加载.

Lua学习(4)——函数

在Lua中函数的调用方式和C语言基本相同,如:print("Hello World")和a = add(x, y).唯一的差别是,如果函数只有一个参数,并且该参数的类型为字符串常量或table的构造器,那么圆括号可以省略,如print "Hello World"和f {x = 20, y = 20}.    Lua为面对对象式的调用也提供了一种特殊的语法--冒号操作符.表达式o.foo(o,x)的另一种写法是o:foo(x).冒号操作符使调用o.foo时将o隐含的作

黑马程序员——c语言学习心得——函数传递二维数组

黑马程序员——c语言学习心得——函数传递二维数组 -------Java培训.Android培训.iOS培训..Net培训.期待与您交流! ------- 一.定义指针的时候一定要初始化.   变量定义的时候给变量初始化,这是保证不出错的一个很好的习惯.尤其是在指针的使用上,如果我们没有给指针初始化,就会出现野指针,该指针的指向并不是我们所希望的,一旦错误的释放了这个指针,就会发生内存的访问.那么如何初始化指针变量呢,一般有以下几种方法:   1.初始化空指针   int* pInteger=N

MySQL基础学习之函数

数学函数 绝对值      abs() 圆周率      PI() 平方根 sqrt() 模除取余   mod(被除数,除数) 随机数      rand() 四舍五入    round(数字) 次方         pow(5,2) e为底的指数函数  exp(数字) 字符串函数 字符长度       length(字符) 字符连接       concat(字符1,字符2) 带符号的字符连接    concat_ws('-','su','zhou') 字符插入      insert('su