以前在学Python时,对于类前的__main__判断有过了解,后来在学习C语言时发现其实都是互通的。所有的程序入口,比如main或者WINmain,在很多编程语言中都以主函数的方式出现。下面为大家整理了一些程序入口的基本概念。
首先,我们的程序进入到入口函数之前,是发生了很多事情的。操作系统的安排,启动运行时库,运行时库再初始化好环境,然后启动你的入口函数,你的程序才正常的运行起来。等你的程序运行结束后,就退回到运行时库,然后再退回到操作系统,然后系统再调度其他程序执行。
在系统把使用权交给我们的这个过程,就是系统安排我们程序运行的过程,也就是准备进入我们程序的入口函数main或者WinMain的过程。操作系统时刻都在运行中,除非你关机断电了。而负责管理各个程序运行的部分就是系统的调度程序。它一直和交通警察一样的,管理进程的运作。当你双击的exe程序时,系统会检测到你的鼠标的动作,从而进行处理。如果发现你双击的是某个exe,系统发现你想要执行一个程序,便会安排让你的程序执行。而这个安排的人就是系统的调度程序。调度程序分析我们的exe,获取程序的类型,然后才能知道我们程序需要什么基础环境。这里说的基础环境,指的是,程序要运行需要的基础运行库。我们用C语言写的程序需要C运行时库,C++的则需要C++运行时库等等,其他的程序自然也需要这些基本库。这些库与系统无关。你在开发时,选用的开发环境和工具,都会决定程序是什么类型,这个与前面说的程序的运行平台不一样。Windows程序运行的平台环境是Windows操作系统,而这个系统中还有各种基础环境,保证这个程序能够正常运行的。一般这些都叫做运行时库。我们用C/C++开发的,如果没有C/C++运行时库的支持,系统就无法启动你的程序了。
什么是C运行时库?
1)C运行时库就是 C run-time library,是 C 而非 C++ 语言世界的概念:取这个名字就是因为你的 C 程序运行时需要这些库中的函数.
2)C 语言是所谓的“小内核”语言,就其语言本身来说很小(不多的关键字,程序流程控制,数据类型等);所以,C 语言内核开发出来之后,Dennis Ritchie 和 Brian Kernighan 就用 C 本身重写了 90% 以上的 UNIX 系统函数,并且把其中最常用的部分独立出来,形成头文件和对应的 LIBRARY,C run-time library 就是这样形成的。
3)随后,随着 C 语言的流行,各个 C 编译器的生产商/个体/团体都遵循老的传统,在不同平台上都有相对应的 Standard Library,但大部分实现都是与各个平台有关的。由于各个 C 编译器对 C 的支持和理解有很多分歧和微妙的差别,所以就有了 ANSI C;ANSI C (主观意图上)详细的规定了 C 语言各个要素的具体含义和编译器实现要求,引进了新的函数声明方式,同时订立了 Standard Library 的标准形式。所以C运行时库由编译器生产商提供。至于由其他厂商/个人/团体提供的头文件和库函数,应当称为第三方 C 运行库(Third party C run-time libraries)。
4)C run-time library里面含有初始化代码,还有错误处理代码(例如divide by zero处理)。你写的程序可以没有math库,程序照样运行,只是不能处理复杂的数学运算,不过如果没有了C run-time库,main()就不会被调用,exit()也不能被响应。因为C run-time library包含了C程序运行的最基本和最常用的函数。
5)到了 C++ 世界里,有另外一个概念:Standard C++ Library,它包括了上面所说的 C run-time library 和 STL。包含 C run-time library 的原因很明显,C++ 是 C 的超集,没有理由再重新来一个 C++ run-time library. VC针对C++ 加入的Standard C++ Library主要包括:LIBCP.LIB, LIBCPMT.LIB和 MSVCPRT.LIB
下面来看一个图示。
图中展示的是一个操作系统的调度程序的示意图。我们双击了exe,系统先捕获的这个动作,将这个请求放入调度队列,然后调度程序再调度运行。调度程序要先要根据程序的类型,来启动对应需要的运行时库,然后才进入到我们程序执行。而这运行时库,是我们程序运行起来的基础支持,就像需要先打开嘴巴,才能吃饭一样。运行时库简单来说,就好像是你这个程序需要的管家。它时刻在关注程序的运行,如果程序崩溃异常,这个运行时库会知道的,从而做出处理。当然,运行时库运行在系统的监控之内。运行时库有点像你的程序的保姆,同时与操作系统保持联系,算是操作系统和你程序的中间联系人。如此来理解一下运行时库,也就不难懂了吧。为什么要做运行时库,因为你程序运行时需要用到这个基本库咯。而这个运行时库,需要由系统来启动运行。
总结来看,我们的程序进入到入口函数之前,是发生了很多事情的。操作系统的安排,启动运行时库,运行时库再初始化好环境,然后启动你的入口函数,你的程序才正常的运行起来。等你的程序运行结束后,就退回到运行时库,然后再退回到操作系统,然后系统再调度其他程序执行。
下面一个简单的程序,从代码上看看这个效果。我们写这个代码如下:
void main()
int i = 0;
然后再这个唯一几句代码里打个断点。光标放在这句代码上,按F9即可。打了断点后,按F5进入调试,调试的界面如下:
这个箭头表示,程序已经进入了我们的程序,那么我们来看看进入的过程的代码执行过程。在VS界面上找到调用哦堆栈小窗口,然后你会找到以下调用堆栈窗口:
如果你看到的不是这样的,有很多问号的,或者显示什么不可用符号等等,在对应的那条上面,右击点击显示或导入“符号”的菜单,然后VS自动更新符号,这样就可以显示出这些函数分符号名了。
堆栈的特点就是先进后出,先进的在底部,这里就是这样的。
执行的顺序从底部到顶部,从顶部可以看出,后面的main()表示正在执行到main函数中了。我们从最底部开始往上看。底部的两条,ntdll.dll是Windows系统的一个核心库,也是系统的核心功能库之一,后面的RtlUserThreadStart表示的就是系统在启动我们的exe,并创建了一个进程主线程。然后,第三句kernel.dll这个库里执行了BaseThreadInitThunk执行了我们的进程的主线程的初始化工作,包括分配线程内存等。
然后基本的系统初始化工作都执行完毕,然后就要开始启动我们的主线程执行了。这个过程就是图中说的启动程序到调度程序做一些初始化工作。接下来就会去启动运行时库。在接下来的五个函数执行中,都可以看到前面ConsoleApplication3开头,这个是我们的程序文件名,这表示这几个函数都是为我们程序服务的,这些都是运行在我们程序的进程空间的,其实就是我们程序所占的内存块中。mainCRTStartup()函数的CRT就是C RunTime(C运行时库)的意思,这里就是C运行时库的函数了,它在准备启动main函数的执行了。不过这里才刚刚启动,是在做初始化运行时环境,就是调用后面的函数__scrt_common_main()。这个函数中做了基本的运行时环境初始化后,又调用__scrt_common_main_seh()。这个函数也做了一系列的初始化工作,然后调用invoke_main()函数,去调用main函数运行。
invoke_main()函数代码如下:
static int __cdecl invoke_main() throw()
return main(__argc, __argv, _get_initial_narrow_environment());
你可以看到,这个就是一个简单的调用而已,就这样就进入了我们的main函数的执行。而对于这个几个函数的代码,你可以直接在调用堆栈中双击就可以看到了。
调用堆栈中,上一个函数是被底下那个函数所调用的,所以这个叫做调用堆栈。
综上所述,你可以从上部分描述中感受到这个过程,在下面的代码级别中,又再一次验证了这个过程,想必对此过程一定更加影响深刻了。而我们的程序代码就是在这个过程完成后,进入到我们的入口函数开始执行的。
然后程序执行完毕后,调用堆栈的函数依次执行完退出,最终又回到了系统的调度函数中执行其他程序。
在接下来的五个函数执行中,都可以看到前面ConsoleApplication3开头,这个是我们的程序文件名,这表示这几个函数都是为我们程序服务的,这些都是运行在我们程序的进程空间的,其实就是我们程序所占的内存块中。操作系统的安排,启动运行时库,运行时库再初始化好环境,然后启动你的入口函数,你的程序才正常的运行起来。如果你看到的不是这样的,有很多问号的,或者显示什么不可用符号等等,在对应的那条上面,右击点击显示或导入“符号”的菜单,然后VS自动更新符号,这样就可以显示出这些函数分符号名了。
函数的概念
先来看第4行代码,这行代码会在显示器上输出“软件开发网”。前面我们已经讲过,puts 后面要带( ),字符串也要放在( )中。
在C语言中,有的语句使用时不能带括号,有的语句必须带括号。带括号的称为函数(Function) 。
C语言提供了很多功能,例如输入输出、获得日期时间、文件操作等,我们只需要一句简单的代码就能够使用。但是这些功能的底层都比较复杂,通常是软件和硬件的结合,还要要考虑很多细节和边界
1)
程序结构是三种: 顺序结构 、选择结构(分支结构)、循环结构。
2)读
程序都要从
main()
入口, 然后从最上面顺序往下读(碰到循环做循环,碰到选择做选择),有且只有一个
main函数。
3)计算机的数据在电脑
中保存是以 二进制的形式. 数据存放的位置就是 他的地址.
4)bit是位 是指为0 或者1。 byte 是指字节, 一个字节 = 八个位.
概念常考到的:
1、编译预处理不是
C语言的一部分,不占运行时间,不要加分号。
C语言编译的
程序称为源
程序,它以ASCII数值存放在文本文件
中。
2、# define PI 3.1415926; 这个写法是错误的,一定不能出现分号。
3、每个
C语言程序中main函数是有且只有一个。
C语言程序设计复习资料 C语言程序设计复习资料全文共12页,当前为第1页。C语言程序设计复习资料全文共12页,当前为第1页。C语言程序设计复习资料 C语言程序设计复习资料全文共12页,当前为第1页。 C语言程序设计复习资料全文共12页,当前为第1页。 总体上必须清楚的: 1)程序结构是三种: 顺序结构 , 循环结构(三个循环结构), 选择结构(if 和 switch) 2)读程序都要从main()入口, 然后从最上面顺序往下读(碰到循环做循环,碰到选择做选择)。 3)计算机的数据在电脑中保存是以 二进制的形式. 数据存放的位置就是 他的地址. 4)bit是位 是指为0 或者1。 byte 是指字节, 一个字节 = 八个位. 5)一定要记住 二进制 如何划成 十进制。 概念常考到的: 1、编译预处理不是C语言的一部分,不再运行时间。C语言编译的程序称为源程序,它以ASCII数值存放在文本文件中。 2、每个C语言程序中main函数是有且只有一个。 3、在函数中不可以再定义函数。 4、算法的是一定要有输出的,他可以没有输入。 5、break可用于循环结构和switch语句。 6、逗号运算符
C语言程序设计;数据类型;字符集是高级语言的编译系统所能识别的字母、数字和特殊符号。每种高级语言都有自己特定的字符集。
C语言的字符集包括:
(1)大、小写英文字母: A ,B,… ,Z,a,b,…z。
(2)数字: 0,1,2,…,9。
(3)运算符: + – * / % > < = & | ? ! ^ ~。
(4)括号:(){ } [ ]。
(5)标点符号: ' " : ;。
(6)特殊符号:\ _ $ #。
(7)空白符:空格符、换行符和制表符。;关键字(Keyword)
又称为保留字,C语言中预先规定的具有固定含义??一些单词
int,return等
标识符(Identifier)
系统预定义标识符,main,printf 等
用户自定义标识符,Add, x, y 等
运算符(Operator)
34种,详见附录 ;分隔符(Separator)
空格、回车/换行、逗号等
大花括号“{”和“}”通常用于标识函数体或者一个语句块
“/*”和“*/”是程序注释所需的定界符
数据(Data)
变量(Variable)
常量(Constant)
; 标识符
定义:用来标识变量、
文章目录一、什么是
函数栈帧二、ESP、EBP、栈的概念 1、栈的概念 2、ESP和EBP 3、认识汇编指令和其他寄存器三、通过编译器了解栈帧的创建和销毁 1、
函数的调用堆栈 2、准备环境和反汇编 3、转到反汇编 (1)为
main函数创建栈帧 (2)进入其他
函数创建栈帧 (3)销毁Add
函数栈帧4、总结几个问题
前言:
函数栈帧是对底层一点基础理解,以下是个人学习的总结,希望对你有帮助。
一、什么是
函数栈帧
比如我们在
C语言中的学习
中,都会创建一个
main函数以及其他拥有独立功能的
函数。
错误 LNK2019 :无法解析的外部符号 _
main,
函数 “int __cdecl invoke_
main(void)” (?invoke_
main@@YAHXZ)
中引用了该符号
错误 LNK1120 :1 个无法解析的外部命令
c语言运行时找不到适当的
程序入口函数
1.项目
中无
入口函数/启动项目
中无
入口函数
2.项目创建问题
如果是Windows
程序,那么Win
Main是
入口函数,在VS2013
中新建项目为“win32项目”,在VS2019
中用“windows桌面向导”创建
参照来源:https://blog.csdn.net/cherisegege/article/details/80297320
ld有多种方法设置进程入口地址, 按一下顺序: (编号越前, 优先级越高)
1, ld命令行的-e选项
2, 连接脚本的ENTRY(SYMBOL)命令
eg. ENTRY(_start) /* Entry point of applicati...
在C语言中,函数的返回值将被保存在一个特殊的寄存器中,这个寄存器称为“返回值寄存器”。当函数返回时,该寄存器中的值将被传递给调用该函数的代码。但是,在某些情况下,我们可能不希望返回值被传递给调用函数,特别是在我们没有明确定义函数返回值类型的情况下。
在main函数中,如果没有显示地返回一个整数,编译器将假定程序返回0。因此,当我们在main函数的末尾调用getchar()函数时,它会等待用户按下任意键,直到用户输入并按下Enter键,这样我们就可以看到程序的输出结果,然后结束程序的执行。这也能够保证程序正常结束,而不是立即退出。
需要注意的是,在某些操作系统上,可能并不需要在main函数的最后调用getchar()函数。因此,这个习惯并不是C语言标准中的要求,但是它是一种好的编程习惯,可以帮助我们更好地控制程序的执行过程和输出结果。