在学习第一个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/lyzaily@126/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)指定。

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

  1. JQuery学习:事件绑定&入口函数&样式控制

    1.基础语法学习: 1.事件绑定 2.入口函数 3.样式控制 <!DOCTYPE html> <html lang="en"> <head> & ...

  2. Egret 学习之 入口函数 及开始编写程序(三)

    1,Egret的程序入口: C和java是以一个main函数作为入口,但egret类似于ActionScript 是以一个文档类作为入口,确切的说是以这个文档类的构造函数作为入口: 2,文档类的构造函 ...

  3. Qt Windows下链接子系统与入口函数(终结版)(可同时存在main和WinMain函数)

    Qt Windows下链接子系统与入口函数(终结版) 转载自:http://blog.csdn.net/dbzhang800/article/details/6358996 能力所限,本讨论仅局限于M ...

  4. C++的入口函数

    我们最开始学习c++时,就知道要写一个main()函数,并且知道这是整个函数的入口,但是c++不只有main()函数这一个入口. 一.对于不同的程序函数入口是不同的. main()是WINDOWS的控 ...

  5. 2、jQuery的基本概念-必看-版本-入口函数- jq对象和dom对象区别

    1.4. jQuery的版本 官网下载地址:http://jquery.com/download/ jQuery版本有很多,分为1.x 2.x 3.x 大版本分类: 1.x版本:能够兼容IE678浏览 ...

  6. 01-老马jQuery教程-jQuery入口函数及选择器

    前言 这套jQuery教程是老马专门为寒门子弟而录制,希望大家看到后能转发给更多的寒门子弟.视频都是免费,请参考课程地址:https://chuanke.baidu.com/s5508922.html ...

  7. u-boot-1.1.6第2阶段入口函数start_armboot分析

    学习目标: 1.分析u-boot-1.1.6第2阶段入口函数void start_armboot (void),熟悉该函数所实现的功能 2.为后面能够掌握u-boot-1.1.6如何启动内核过程打下基 ...

  8. [C#] 了解过入口函数 Main() 吗?带你用批处理玩转 Main 函数

    了解过入口函数 Main() 吗?带你用批处理玩转 Main 函数 目录 简介 特点 方法的参数 方法的返回值 与批处理交互的一个示例 简介 我们知道,新建一个控制台应用程序的时候,IDE 会同时创建 ...

  9. JavaScript学习09 函数本质及Function对象深入探索

    JavaScript学习09 函数本质及Function对象深入探索 在JavaScript中,函数function就是对象. JS中没有方法重载 在JavaScript中,没有方法(函数)重载的概念 ...

随机推荐

  1. JS-基础2

    JS基本语法   1.学习javascript的目的? A.增强网页的动态效果. B.改变网页中的元素(能够直接对网页中的元素进行操作). C.加强同后台的数据交互.页面的数据验证. 2.JS在web ...

  2. zabbix 报警方式之 邮件报警(4)

    一.为什么要自定义邮件脚本报警? 灵活,方便.可以自定义过滤信息. 下面是使用不同方式的邮件报警,一个是利用sendEmail程序来发送报警邮件,第二个是利用python脚本来发送邮件. 二.send ...

  3. OD 实验(十六) - 从对话框入手对程序的逆向

    对话框: 对话框从类型上分为两类:modal 对话框和 modeless 对话框,就是模态对话框和非模态对话框,也有叫成模式和非模式 模态对话框不允许用户在不同窗口间进行切换,非模态对话框允许用户在不 ...

  4. 二、jenkins配置email(以腾讯企业qq为例)

    废话不多说,直接上干货: 主要针对两个部分进行介绍: 1.jenkins内置的邮件功能: 2.Editable Email Notification插件的邮件功能: 低版本的jenkins有很多插件都 ...

  5. 第十篇 before_request after_request

    Flask我们已经学习很多基础知识了,现在有一个问题 我们现在有一个 Flask 程序其中有3个路由和视图函数,如下: from flask import Flask app = Flask(__na ...

  6. 使用COM口的2、3针的通断作为中端源(有一个读图像的摄像头,当把卡插到位时触发中端,防止在插卡的过程中出现不稳定的图像)

    利用串口2读,串口3发数据的特点.建立不断的发送流,再从接收端接收.如果收到,则数据畅通,否则断开.相当于产生一个中断.这样电脑对外部事件可作出反应. using System;using Syste ...

  7. Maven父级pom.xml配置文件

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/20 ...

  8. linux系统构架 - LB集群之LVS的DR设置

    在lvs的nat模式的基础上 1.清空ipvsadm规则 ipvsadm -C 查看 ipvsadm -ln 2.清空iptables规则 iptables -t nat -F 3.修改rs的网卡配置 ...

  9. javascript变量,作用域和内存问题

    1:ECMAScript所有函数的参数都是按值传递的 function setName(obj){ obj.name="finn"; obj=new Object(); obj.n ...

  10. Redis搭建(六):Redis持久化配置

    一.介绍 Redis的持久化有2种方式: Rdb快照 Aof日志 1. Rdb快照的配置选项 save 900 1 // 900内,有1条写入,则产生快照 save 300 1000 // 如果300 ...