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\
参考一下文摘:
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++学习--入口函数的更多相关文章
- JQuery学习:事件绑定&入口函数&样式控制
1.基础语法学习: 1.事件绑定 2.入口函数 3.样式控制 <!DOCTYPE html> <html lang="en"> <head> & ...
- Egret 学习之 入口函数 及开始编写程序(三)
1,Egret的程序入口: C和java是以一个main函数作为入口,但egret类似于ActionScript 是以一个文档类作为入口,确切的说是以这个文档类的构造函数作为入口: 2,文档类的构造函 ...
- Qt Windows下链接子系统与入口函数(终结版)(可同时存在main和WinMain函数)
Qt Windows下链接子系统与入口函数(终结版) 转载自:http://blog.csdn.net/dbzhang800/article/details/6358996 能力所限,本讨论仅局限于M ...
- C++的入口函数
我们最开始学习c++时,就知道要写一个main()函数,并且知道这是整个函数的入口,但是c++不只有main()函数这一个入口. 一.对于不同的程序函数入口是不同的. main()是WINDOWS的控 ...
- 2、jQuery的基本概念-必看-版本-入口函数- jq对象和dom对象区别
1.4. jQuery的版本 官网下载地址:http://jquery.com/download/ jQuery版本有很多,分为1.x 2.x 3.x 大版本分类: 1.x版本:能够兼容IE678浏览 ...
- 01-老马jQuery教程-jQuery入口函数及选择器
前言 这套jQuery教程是老马专门为寒门子弟而录制,希望大家看到后能转发给更多的寒门子弟.视频都是免费,请参考课程地址:https://chuanke.baidu.com/s5508922.html ...
- 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如何启动内核过程打下基 ...
- [C#] 了解过入口函数 Main() 吗?带你用批处理玩转 Main 函数
了解过入口函数 Main() 吗?带你用批处理玩转 Main 函数 目录 简介 特点 方法的参数 方法的返回值 与批处理交互的一个示例 简介 我们知道,新建一个控制台应用程序的时候,IDE 会同时创建 ...
- JavaScript学习09 函数本质及Function对象深入探索
JavaScript学习09 函数本质及Function对象深入探索 在JavaScript中,函数function就是对象. JS中没有方法重载 在JavaScript中,没有方法(函数)重载的概念 ...
随机推荐
- kubernetes 核心原理
3.1 K8s API Server 原理分析 K8s API server核心提供对各种资源对象的增.删.改.查以及Watch等HTTPRest接口,是集群内各个模块之间数据交互和通信的中心枢纽,是 ...
- Change R source code
If you'd like to simply test out the effect of that change in an interactive R session, you can do s ...
- 【Java】编程
3.Java I/O流输入输出,序列化,NIO,NIO.2 https://www.cnblogs.com/jiangwz/p/9193776.html 4.JAVA调用WCF(转) https:// ...
- 深入浅出 Java Concurrency (11): 锁机制 part 6 CyclicBarrier
如果说CountDownLatch是一次性的,那么CyclicBarrier正好可以循环使用.它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point).所谓屏障 ...
- C# 接口(Interface)
接口定义了所有类继承接口时应遵循的语法合同.接口定义了语法合同 "是什么" 部分,派生类定义了语法合同 "怎么做" 部分. 口定义了属性.方法和事件,这些都是接 ...
- [Z]用subcaption包排版子图(表)与图(表)格式设置
很不错的一篇文章,可以进一步参考caption和subcaption的文档: http://www.peteryu.ca/tutorials/publishing/latex_captions
- data-ajax="false"
转自:https://yq.aliyun.com/ziliao/265393 最近在做一个项目,由于涉及到跨平台性,所以采用了jquerymobile这个框架,在开发过程中,一开始为了图测试方便,采用 ...
- Hexo+Github/Coding免费搭建个人博客网站
体验更优排版请移步原文:http://blog.kwin.wang/other/hexo-github-build-blog.html 很早之前就想搭建一个属于自己的博客网站,一方面是给自己做笔记,把 ...
- npm 全局环境变量配置
我们要先配置npm的全局模块的存放路径以及cache的路径,例如我希望将以上两个文件夹放在NodeJS的主目录下,便在NodeJs下建立”node_global”及”node_cache”两个文件 ...
- 运维自动化工具 Cobbler
简介: 关于操作系统安装方面的自动化,早前我们使用 RedHat 推出的 Kickstart 来批量安装操作系统,近年来 RedHat 又推出一个 Cobbler . Cobbler 使用 Pytho ...