MDK main函数运行前的详细分析
main 方法
main 方法首先,让我们来看一下 main 方法的基本语法。
在Java中,main 方法是程序的入口点,它的语法格式如下:```java。
public class Main {。
public static void main(String[] args) {。
// 在这里编写你的程序代码。
}。
}。
```。
在这段代码中,我们定义了一个名为 Main 的类,并在其中定义了一个名为main 的方法。
该方法被声明为 public(公共的),static(静态的)和 void(无返回值)。
它接受一个类型为 String 数组的参数 args。
在 main 方法中,我们可以编写我们的程序代码,以实现我们想要的功能。
接下来,让我们来看一下 main 方法的作用。
作为程序的入口,main 方法是程序开始执行的地方。
当我们运行一个 Java 程序时,虚拟机会自动查找并执行名为main 的方法。
因此,我们可以将 main 方法看作是程序的起点,所有的程序逻辑都将从这里开始执行。
除了作为程序的入口,main 方法还可以接受命令行参数。
在上面的语法中,我们可以看到 main 方法接受一个类型为 String 数组的参数 args。
这些参数可以在程序运行时从命令行传入,我们可以根据这些参数来定制程序的行为。
在实际应用中,我们经常会用到 main 方法来执行一些初始化操作,比如初始化变量、加载配置文件等。
此外,我们还可以在 main 方法中调用其他方法,实现程序的逻辑功能。
总之,main 方法是程序的核心,它承担着程序执行的重要任务。
在使用 main 方法时,我们需要注意一些细节。
首先,main 方法必须被声明为public 和 static,这是因为虚拟机需要通过这个方法来启动程序。
其次,main 方法的参数类型必须是 String 数组,这是因为命令行参数都以字符串的形式传入。
最后,main 方法没有返回值,它的返回类型被声明为 void。
main函数原型
main函数原型Main函数原型是编程语言中的一个重要概念,它定义了程序的入口点和参数。
在这篇文章中,我们将深入探讨main函数的原型,包括其语法、参数和返回值等方面。
让我们来了解一下main函数的语法。
在大多数编程语言中,main 函数的原型通常是这样的:int main(int argc, char *argv[])其中,int表示main函数的返回值类型,通常为整数类型;argc 表示命令行参数的个数;char *argv[]表示命令行参数的数组。
接着是参数int argc,它表示命令行参数的个数。
命令行参数是指在程序运行时从命令行中输入的参数,比如在命令行中输入"program.exe arg1 arg2",其中arg1和arg2就是命令行参数。
argc参数的值为命令行参数的个数加1,其中包括程序名称本身。
最后是参数char *argv[],它表示命令行参数的数组。
argv数组中的每个元素都是一个字符串,表示一个命令行参数。
argv[0]表示程序的名称,argv[1]、argv[2]等表示其他命令行参数。
通过遍历argv数组,我们可以获取每个命令行参数的值,从而对程序进行相应的操作。
在main函数中,我们可以根据需要使用命令行参数来控制程序的行为。
比如,我们可以根据命令行参数的值来判断程序是执行某个功能还是执行另一个功能;或者我们可以根据命令行参数的值来读取相应的输入文件或输出文件等等。
除了命令行参数,main函数还可以使用其他方式来获取输入。
比如,我们可以使用标准输入流stdin来获取用户的输入,然后根据输入来执行相应的操作。
类似地,main函数也可以使用标准输出流stdout来输出结果。
在编写main函数时,我们需要注意一些细节。
首先,我们应该对命令行参数的个数进行合理的判断和处理,以避免出现数组越界的错误。
其次,我们应该对命令行参数的值进行合法性检查,以防止恶意输入或非法操作。
关于main函数的叙述
关于main函数的叙述main函数是C语言中的一个非常重要的函数,它是程序的入口点,也是程序执行的起始点。
在一个C语言程序中,必须有且只能有一个main函数。
本文将从几个方面详细介绍关于main函数的一些重要内容。
main函数是C语言程序的入口函数。
当程序被运行时,操作系统会首先调用main函数开始执行程序。
main函数的定义通常是这样的:int main() {}。
其中,int是main函数的返回类型,表示main 函数执行完毕后会返回一个整数值;main是函数名;空括号表示main函数不接收任何参数。
我们可以根据需要给main函数添加参数,例如int main(int argc, char *argv[]),这样main函数就可以接收命令行参数。
main函数的返回值可以用来表示程序的执行状态。
根据C语言标准,main函数的返回值可以是0或者非0的整数。
返回值为0通常表示程序执行成功,而非0的返回值则表示程序执行失败或出现了错误。
我们可以根据main函数的返回值来判断程序的执行结果,从而进行相应的处理。
比如,在shell脚本中,可以根据程序的返回值来判断是否执行成功。
main函数可以调用其他函数来完成特定的任务。
在C语言中,我们可以在main函数内部调用其他函数,这些函数可以是由我们自己定义的,也可以是系统提供的函数库中的函数。
通过调用其他函数,我们可以将程序的功能模块化,提高代码的可读性和可维护性。
例如,我们可以将一些复杂的计算逻辑封装成一个函数,然后在main函数中调用这个函数来完成相应的计算。
main函数还可以接收命令行参数。
在命令行中执行程序时,可以给程序传递一些参数,这些参数可以在main函数中进行处理。
命令行参数通常以字符串的形式传递给main函数,我们可以使用argc 和argv两个参数来接收这些参数。
其中,argc表示命令行参数的个数,argv是一个指针数组,每个元素指向一个命令行参数的字符串。
c语言main函数
c语言main函数C语言是一门广泛使用的编程语言,它的主要特点是简单易学、功能强大、易于移植等等。
而在C语言中,main函数则是最为重要的部分之一。
本文将详细介绍C语言中的main函数,包括其定义、作用、参数、返回值等等。
一、main函数的定义在C语言中,main函数是程序的入口点,也就是程序执行的第一个函数。
它的定义方式如下:```int main(int argc, char *argv[]){// 函数体return 0;}```其中,int表示函数返回值的类型,main表示函数名,括号中的两个参数分别是整型变量argc和字符型指针变量argv[]。
大括号内是函数的具体实现。
二、main函数的作用main函数是程序的起点,它的作用是为程序提供一个执行的入口。
当程序运行时,操作系统会调用main函数,并执行其中的代码。
因此,main函数的作用是初始化程序,分配内存空间,读取输入数据,处理逻辑等等。
三、main函数的参数在main函数中,argc和argv[]是两个重要的参数。
它们分别表示命令行参数的个数和具体的参数列表。
argc是一个整型变量,用于记录命令行参数的个数。
例如,如果在命令行中输入了“./program arg1 arg2 arg3”,那么argc的值就为4,因为除了程序名之外,还有三个参数。
argv[]是一个字符型指针数组,用于存储具体的命令行参数。
例如,如果在命令行中输入了“./program arg1 arg2 arg3”,那么argv[0]的值就是程序名“./program”,argv[1]的值是“arg1”,argv[2]的值是“arg2”,argv[3]的值是“arg3”。
四、main函数的返回值在C语言中,main函数的返回值是一个整型值。
它表示程序的执行结果,通常有以下两种情况:1. 返回0:表示程序执行成功,没有错误发生。
2. 返回非0值:表示程序执行失败,发生了某些错误。
dlmain使用技巧
dlmain使用技巧在进行编程开发时,我们经常会遇到需要使用main函数的情况。
main函数是每个C/C++程序的入口函数,它是程序开始执行的地方。
下面是一些使用main函数的技巧。
1. 程序的执行逻辑:在编写程序时,我们需要考虑程序的执行逻辑。
我们可以使用多个函数实现不同的功能,然后在main函数中按照顺序调用这些函数。
这样可以使程序结构清晰,易于阅读和维护。
2. 命令行参数:main函数可以接受命令行参数。
通过命令行参数,我们可以在程序运行时向程序传递一些参数。
在main函数的参数列表中,argc表示命令行参数的个数,而argv是一个字符串数组,其中存储了这些参数的值。
利用这些参数,我们可以实现程序的不同功能和配置。
3. 程序的返回值:main函数可以有返回值,它可以用来告诉操作系统程序的执行结果。
一般来说,返回0表示程序正常退出,而非零值表示程序出现了错误或异常情况。
通过返回值,可以方便地在脚本或其他编程语言中调用和处理C/C++程序。
4. 调试技巧:在调试程序时,我们经常会使用断点来暂停程序的执行,然后逐步跟踪代码的执行过程。
在main函数中设置断点,可以方便地查看程序执行到哪个位置,变量的值是多少,以及函数的调用顺序等。
这样可以快速定位和解决程序的问题。
5. 程序的异常处理:在程序出现异常情况时,我们可以使用try-catch语句来捕获和处理异常。
将主要的程序逻辑放在try块中,当程序抛出异常时,可以在catch块中进行处理。
这样可以保证程序的健壮性和可靠性,避免程序崩溃或产生不确定的行为。
6. 程序的多线程处理:在程序需要处理大量数据或并行计算时,我们可以使用多线程来提高程序的性能和效率。
在main函数中创建多个线程,每个线程负责一部分任务,然后通过线程间的通信来实现数据共享和同步。
这样可以有效地利用计算资源,加速程序的运行。
7. 程序的模块化设计:在大型项目中,我们通常会使用模块化设计来组织代码。
C++main函数与命令行参数,退出程序
C++main函数与命令⾏参数,退出程序 (除动态链接库dll,静态链接库lib⼯程外)所有的C++程序都必须有⼀个main函数。
如果你编译⼀个没有main函数的C++exe⼯程,编译器会提⽰错误。
main函数是你的代码开始执⾏的地⽅,但在main函数调⽤前,所有的没有被显⽰初始化的static类成员都被设置为零。
在微软C++中,调⽤main函数前全局静态对象也会被初始化。
main函数相对于其他函数⽽⾔有以下不同之处:1. 不能被重载2. 不能被声明为inline,内联函数3. 不能被声明为static4. 不能获取它的地址,Cannot have its address taken5. 不能被调⽤ main函数并没有声明,因为它是语⾔内置的。
如果有的话,其形式如下所⽰:int main();int main(int argc, char *argv[], char *envp[]);Microsoft Specific 如果你的代码⽂件使⽤的是Unicode wide character,你可以使⽤wmain,是main函数的宽字符版本,其声明如下:int wmain( );int wmain(int argc, wchar_t *argv[], wchar_t *envp[]); 你还可以使⽤_tmain,该函数定义在tchar.h。
_tmain被解释为main,除⾮你定义了_UNICODE(在这种情况下,_tmain被解释为wmain)。
如果main函数的返回值没有指定,编译器会提供⼀个值为零的返回值。
另外,main wmain可以被声明为返回为void(没有返回值)。
如果你将main wmain声明为void,那么你就不能通过return语句将exit code返回给程序的⽗进程或系统。
在main wmain是void的时候,你需要使⽤the exit function来返回exit code。
main函数参数解析
main函数参数解析int argc,char *argvagrc表⽰参数的个数argv储存参数这个函数的意思是逐⼀输出参数实际上,main函数也可以带参数。
带参数main函数的定义格式如下:void main(int argc, char *argv[]){... ...}argc和argv是main函数的形式参数。
这两个形式参数的类型是系统规定的。
如果main函数要带参数,就是这两个类型的参数;否则main函数就没有参数。
变量名称argc和argv是常规的名称,当然也可以换成其他名称。
那么,实际参数是如何传递给main函数的argc和argv的呢?我们知道,C程序在编译和链接后,都⽣成⼀个exe⽂件,执⾏该exe⽂件时,可以直接执⾏;也可以在命令⾏下带参数执⾏,命令⾏执⾏的形式为:可执⾏⽂件名称参数1 参数2 ... ... 参数n可执⾏⽂件名称和参数、参数之间均使⽤空格隔开。
例如,我们在DOS下运⾏copy c:\test.txt d:\test.txt,可执⾏⽂件名称为copy,参数1为字符串“c:\test.txt”,参数2为“d:\test.txt”。
结果copy命令将c:\test.txt拷贝到d盘,⽬标⽂件取为test.txt。
如果按照这种⽅法执⾏,命令⾏字符串将作为实际参数传递给main函数。
具体为:(1) 可执⾏⽂件名称和所有参数的个数之和传递给argc;(2) 可执⾏⽂件名称(包括路径名称)作为⼀个字符串,⾸地址被赋给argv[0],参数1也作为⼀个字符串,⾸地址被赋给argv[1],... ...依次类推。
例如,现在运⾏命令⾏(test是编译后的exe⽂件名称):C:\TC\test how are you那么test⼯程的main函数参数argc=4;argv[0]将保存字符串"C:\TC\test"的⾸地址;argv[1]将保存字符串"how"的⾸地址;argv[2]将保存字符串"are"的⾸地址;argv[3]将保存字符串"you"的⾸地址;下⾯的例⼦打印所有的argv参数:main(int argc, char *argv[]){int i;printf("\nTotal %d arguments",argc);for(i=0;i<argc;i++){printf("\nArgument %d = %s ",i+1, argv[i]);}}如果编译后的exe⽂件名称为test,在DOS下运⾏C:\TC\test how are you,结果输出:Total 4 argumentsArgument 1 = C:\TC\test.exeArgument 2 = howArgument 3 = areArgument 4 = you我们也可以在调试状态下输⼊命令⾏参数,⽅法是:在TurboC的Options菜单下有⼀个⼦菜单Arguments,选择该项并确定,弹出输⼊窗⼝;在输⼊窗⼝键⼊命令⾏参数即可。
C51–在main__之前到底做了什么
1C51–在main()之前到底做了什么??用C 写过东西的都知道,程序是从main()函数开始执行的,在main()之前会有一系列的初始化工作,那么这些初始化到底做了些什么了??大家也都知道在MCU 中,程序是在Flash 里面,而数据是在RAM 里面,掉电之后,Flash 里面的东西还在,RAM 里面的东西就没有了.那么你把程序下载到芯片里面之后,数据是在哪里了?你会想,它当然是在Flash 里面的.那数据又是在什么时候跑到RAM 里面了呢?结合C51生成的M51文件,大概的说一下main()函数之前,到底都做了哪些初始化的工作.程序编译(assembly,compile),连接(link)过后会生成HEX 文件.整个HEX 文件是由程序和数据(主要是global 和static 的数据)组成的.当然程序和数据会放在不同的地址,这些地址是绝对的地址.绝对的地址是在连接(link)过程中生成的.如果是C51的话,何以看一下他的M51文件.M51文件就是link 的MAP 文件. 例如:MCU 是C8051F340,IDE 是uVersion4,C51是v9.01,main()函数如下://main.cunsigned int a = 0;unsigned int b = 0xff;void main(void){unsigned int c;unsigned int d;d = c;while(1);} build output 的信息是:Build target ‘Target 1′assembling STARTUP.A51…compiling main.c…linking…Program Size: data=17.0 xdata=0 code=160“test” – 0 Error(s), 0 Warning(s).可以看到,data用里17Byte,code是160Byte,没有用到xdata.查看M51文件:BL51 BANKED LINKER/LOCATER V6.22 04/25/2010 11:27:23 PAGE 1BL51 BANKED LINKER/LOCATER V6.22, INVOKED BY:D:\PROGRAM FILES\KEIL\C51\BIN\BL51.EXE STARTUP.obj, main.obj TO test RAMSIZE (256)MEMORY MODEL: SMALLINPUT MODULES INCLUDED:STARTUP.obj (?C_STARTUP)main.obj (MAIN)D:\PROGRAM FILES\KEIL\C51\LIB\C51S.LIB (?C_INIT)LINK MAP OF MODULE: test (?C_STARTUP)TYPE BASE LENGTH RELOCATION SEGMENT NAME—————————————————–* * * * * * * D A T A M E M O R Y * * * * * * *REG 0000H 0008H ABSOLUTE “REG BANK 0″DATA 0008H 0004H UNIT ?DT?MAINDATA 000CH 0004H UNIT _DATA_GROUP_IDATA 0010H 0001H UNIT ?STACK* * * * * * * C O D E M E M O R Y * * * * * * *CODE 0000H 0003H ABSOLUTECODE 0003H 008CH UNIT ?C_C51STARTUPCODE 008FH 0009H UNIT ?C_INITSEGCODE 0098H 0008H UNIT ?PR?MAIN?MAIN说一下里面的意义,首先是DATA MEMORY,REG是特殊寄存器(SFR)R0-R7,SFR是映射到RAM上的.BASE是0000H,LENGTH是0008H.后面是DATA,从0008H开始,LENGTH是0004H,TYPE是UINT,这个是全局变量a,b在RAM中的位置.2后面是_DATA_GROUP_,是存放临时变量时用的,_DATA_GROUP_在C51中解释的原话是“The _DATA_GROUP_ is a segment that contains all the automatic variables and function arguments from your program that are stored in DATA memory.These segments are created so that the linker can perform overlay analysis and better optimize your use of DATA and BIT memory.”上面的意思是说_DATA_GROUP_好像是执行overlay analysis的.我现在还不清楚的是它的具体作用,还有他和SP指向的栈有什么区别?我记得好像是函数的局部变量是放在栈里面的,函数退出的时候,会执行POP命令.因为我在main()里面声明了两个局部变量,这两个变量是放在_DATA_GROUP_里面的吗??后面的IDATA是堆栈的位置.堆栈在RAM中的0×0010的地方,但是LENGTH只有0001H,不知道为什么是这样的?CODE MEMORY中开始是0000H,也是绝对地址0,一上电,PC指针指向的地方,这个地方肯定有一个LJMP,跳转到STARTIP1这个标号的位置,这个指令占三个字节.后面的是C_C51STARTUP段,这个段就是startup.a51中的内容,0003H也就是STARTUP1在Flash中的位置,程序上电后就跳到这个地方,主要的作用是初始化内存,把RAM中00H-7FH全部清零.后面的是C_INITSEG段,看一下?C_INITSEG原话解释:The initial values of global and static variables are stored in ROM in a segment called ?C_INITSEG. They are then copied to the relevent RAM locations after the code in STARTUP.A51 has been executed and before the main() function is called. The code that performs the variable initialization may be found in the file INIT.A51 inside the LIB folder.从中知道就是把global and static variables 在ROM中的段就是?C_INITSEG段.那么这个段中的数据时怎么到RAM中区的呢?把这个段中的数据copy到RAM中去,看?C_INITSEG解释的最后一句,使用的文件是INIT.A51,但是现在好像不是显式的添加这个文件,而是用的库文件,可以在M51的头部看到,INPUT MODULES INCLUDED:STARTUP.obj (?C_STARTUP)main.obj (MAIN)D:\PROGRAM FILES\KEIL\C51\LIB\C51S.LIB (?C_INIT)也就是说在STARTUP.A51之后,在main()之前,执行的是INIT.A51的程序,也就是把ROM中的数据copy到RAM中区.这个之后,main()函数之前的所有准备工作也就做完了.可以去执行main()函数了.小结:以前一直没有搞清楚这个程序到底是怎么运行的,隐约的觉得数据一开始是在Flash中的,但是什么时候跑到RAM中去了呢??现在基本搞清楚了.下面想仔细研究一下在main()函数之前执行的程序究竟是什么意思.主要也就是STARTUP.A51和INIT.A51.结合A51和C51的datasheet看.3。
c语言中main函数用法及知识点总结
c语⾔中main函数⽤法及知识点总结1、main函数是C程序的⼊⼝函数,即程序的执⾏从main函数开始,其他函数的调动也直接或间接地在main函数中调⽤。
2、main函数的返回值⽤于解释程序的退出状态。
若返回0,则表⽰程序正常退出。
返回其他数字的含义由系统决定。
通常返回⾮零代表程序异常退出。
实例#include <stdio.h>#include <string.h>int main(int argc, char **argv) {int i = 0;printf("The program name is %s\n", argv[0]);printf("The command line has %d argument: \n", argvc - 1);for (i = 1; i < argc; i++) {printf("%s ", argv[i]);}return 0;}知识点扩充:每⼀C程序都必须有⼀main()函数,可以根据⾃⼰的爱好把它放在程序的某个地⽅。
有些程序员把它放在最前⾯,⽽另⼀些程序员把它放在最后⾯,⽆论放在哪个地⽅,以下⼏点说明都是适合的。
在Turbo C2.0启动过程中,传递main()函数三个参数:argc,argv和env。
* argc:整数,为传给main()的命令⾏参数个数。
* argv:字符串数组。
char* argv[],我们可以看出,argv的类型是char* [],即是⼀个指向字符数组的指针,所以我们还可以写作:char** argv。
在DOS 3.X版本中,argv[0]为程序运⾏的全路径名;对DOS 3.0以下的版本,argv[0]为空串("")。
argv[1]为在DOS命令⾏中执⾏程序名后的第⼀个字符串;argv[2]为执⾏程序名后的第⼆个字符串;...argv[argc]为NULL。
KEIL中如何程序在RAM中运行
KEIL中如何程序在RAM中运行前言最近老是遇到使用KEIL时需要将部分或者全部程序放到RAM中运行的问题。
故此花了不少时间搜索资料和几番尝试,现将其总结在本篇文章中,也是为大家以后的工作节省时间罢。
本文中会介绍通过STM32F411Nucleo的一个例子来介绍几种让程序在RAM中运行的方法。
在该例子中,通过调用ToggleLED函数来翻转LED2亮灭。
接下来,我们将通过多种方法将这段代码放在RAM中运行。
ToggleLED函数从Flash中执行的情况我们先来看看T oggleLED函数从Flash中执行的情况。
下面是ToggleLED函数和它的调用情况。
在main函数的while(1)里调用ToggleLED。
Linker的配置为,见下图:Flash起始地址:0x08000000RAM起始地址:0x20000000编译后从map文件可以看到,ToggleLED以及其中调用到的HAL_GPIO_TogglePin和HAL_Delay函数的地址都在FLASH中。
将翻转LED的程序放到SRAM中执行方法一:通过#pragma arm section code = “RAMCODE ”和#pragma arm section。
参考Example1代码。
这种方式,可以同时将多个函数放到指定的section。
具体方法如下:1. 修改.sct文件,自定义一个叫做RAMCODE的section,放在RW_IRAM1执行区域,地址范围0x20000000~0x20020000。
2. 在工程中使用前面修改的.sct文件3.以#pragma arm section code = “RAMCODE” 开头,以#pragma arm section结尾。
将所有需要放到RAMCODE section的函数包括进来。
编译时,编译器会自动将这些函数放到RAMCODE所在0x20000000开始的区域。
4.从map文件里,可以看到这四个函数都已经被放到了SRAM中。
main函数 含义
main函数含义在计算机编程领域中,main函数是一种非常重要的东西。
它是程序的一个入口点,是程序的第一扇门,它负责启动程序和管理程序的执行。
也就是说,当我们写一个程序时,首先要定义一个main函数。
那么,main函数的具体含义是什么呢?1.定义main函数是C和C++语言中的一个关键字,可以理解为程序的主体函数。
它主要包含程序的执行内容和流程,程序运行时从main函数开始,执行完main函数后再结束。
在执行前,操作系统会自动调用main函数,将控制权交给程序。
2.功能由于main函数是程序的主体函数,其在程序中具有以下几个基本功能:(1)初始化:main函数在程序启动时负责进行初始化工作,比如创建变量、分配内存等。
(2)执行程序:main函数是程序的入口点,其主要任务是执行程序的指令,读取用户输入,对数据进行处理和输出结果。
(3)结束程序:程序运行结束后,main函数也将随之结束,释放系统资源并关闭程序。
3.语法main函数的语法格式和使用方法有一定的差别,主要取决于使用的语言。
下面分别介绍C和C++语言中main函数的语法。
(1)C语言中的main函数在C语言中,main函数的语法格式为:int main(void){/*程序执行内容*/return 0;}其中,int代表函数返回一个整数类型的值,main为函数名,void表示函数不接收任何参数,return 0表示函数执行结束并返回一个整数值0。
(2)C++语言中的main函数在C++语言中,main函数的语法格式与C语言类似,但是有些细节上有些不同:int main(int argc, char **argv){/*程序执行内容*/return 0;}其中,argc表示命令行参数的数量,argv表示命令行参数,这些参数可以由用户在运行程序时输入。
此外,在C++语言中,main函数也可以返回一个值,但是返回值类型可以是除了void、int、和char以外的其他类型。
Keil程序运行main函数之前的汇编
Keil开发的ARM程序main函数之前的汇编分析——BIN文件中RW段的数据移动系统平台:STM32系列STM32F103ZE,512KB内部FLASH,64KB片内存储;FLASH地址范围0x0800 0000 ~ 0x0808 0000,用于存放代码;片内存储地址范围0x2000 0000 ~ 0x2001 0000,用于存放数据;Cortex-M3上电后来到复位中断(已将前4个字节的值存入MSP堆栈指针),转到__main 标号,完成RW段的移动、ZI段的初始化,建立堆栈,初始化库函数,然后跳转到main函数,开启C程序之旅,执行流程如图1所示。
图1 main函数之前的汇编程序执行流程图本文主要讨论RW段的移动,RW段就是程序中赋了初值的变量,它的搬移我看到过两种方式。
在BIN文件中,RO段和RW段之间有8个双字的Region$$Table,4个双字一组,分别用于完成RW段的搬移和ZI段的初始化。
(1)__scatterload_copy来完成此时RW段的内容保存到内存开始的地方,本文中是0x20000000,用这一方式完成后,内存中存放的不是RW数据的内容,而是一个地址。
这个地址是在FLASH中,即指向了其在RO段的地址,实际的内容是在RO段中。
(2)通过__uncompressed1实现RW是程序中初始化的变量,但是这些变量有可能初值是0,因此为了节省空间,实际在BIN文件中RW段是压缩过的。
调用__uncompressed1解压缩RW段的数据内容,并将其保存到内存开始的地方。
图2 BIN文件中压缩RW段内容图2是BIN文件中RW段的数据内容,影印部分显示,大小是164字节。
其中0x0001 C72C前面8个双字的内容是Region$$Table,将其列出如下。
0x0801 C72C BIN文件中RW段的开始地址0x2000 0000 RW段要存放到RAM中的地址0x0000 0334 要存放到RAM中的RW段数据大小0x0800 0184 执行函数__scatterload_copy或者__uncompressed1上面4个双字完成RW段的搬移。
Keil程序运行main函数之前的汇编
Keil开发的ARM程序main函数之前的汇编分析——BIN文件中RW段的数据移动系统平台:STM32系列STM32F103ZE,512KB内部FLASH,64KB片内存储;FLASH地址范围0x0800 0000 ~ 0x0808 0000,用于存放代码;片内存储地址范围0x2000 0000 ~ 0x2001 0000,用于存放数据;Cortex-M3上电后来到复位中断(已将前4个字节的值存入MSP堆栈指针),转到__main 标号,完成RW段的移动、ZI段的初始化,建立堆栈,初始化库函数,然后跳转到main函数,开启C程序之旅,执行流程如图1所示。
图1 main函数之前的汇编程序执行流程图本文主要讨论RW段的移动,RW段就是程序中赋了初值的变量,它的搬移我看到过两种方式。
在BIN文件中,RO段和RW段之间有8个双字的Region$$Table,4个双字一组,分别用于完成RW段的搬移和ZI段的初始化。
(1)__scatterload_copy来完成此时RW段的内容保存到内存开始的地方,本文中是0x20000000,用这一方式完成后,内存中存放的不是RW数据的内容,而是一个地址。
这个地址是在FLASH中,即指向了其在RO段的地址,实际的内容是在RO段中。
(2)通过__uncompressed1实现RW是程序中初始化的变量,但是这些变量有可能初值是0,因此为了节省空间,实际在BIN文件中RW段是压缩过的。
调用__uncompressed1解压缩RW段的数据内容,并将其保存到内存开始的地方。
图2 BIN文件中压缩RW段内容图2是BIN文件中RW段的数据内容,影印部分显示,大小是164字节。
其中0x0001 C72C前面8个双字的内容是Region$$Table,将其列出如下。
0x0801 C72C BIN文件中RW段的开始地址0x2000 0000 RW段要存放到RAM中的地址0x0000 0334 要存放到RAM中的RW段数据大小0x0800 0184 执行函数__scatterload_copy或者__uncompressed1上面4个双字完成RW段的搬移。
main函数中各参数的含义
main(int argc, char *argv[], char *env[])中各参数的含义argv为指针的指针argc为整数char **argv or: char *argv[ ] or: char argv[ ][ ]main()括号内是固定的写法。
argv是一个指针数组,他的元素个数是argc,存放的是指向每一个参数的指针,他的第一个元素即argv[0]为编译生成的可执行文件名(包括路径eg:"F:\VC\Ex1\Debug\Ex1.exe"),从二个元素(argv[1])开始,是每一个参数.int argc 表示argv的大小,是实际参数个数+1,其中+1是因为argv[0]是编译后的可执行文件名下面给出一个例子来理解这两个参数的用法:假设程序的名称为prog,1.当只输入prog,则由操作系统传来的参数为:argc=1,表示只有一程序名称。
argc只有一个元素,argv[0]指向输入的程序路径及名称:./prog2.当输入prog para_1,有一个参数,则由操作系统传来的参数为:argc=2,表示除了程序名外还有一个参数。
argv[0]指向输入的程序路径及名称。
argv[1]指向参数para_1字符串。
*env: 字符串数组。
env[] 的每一个元素都包含ENVV AR=value形式的字符串。
其中ENVV AR为环境变量如PA TH或87。
value 为ENVV AR的对应值如C:\DOS, C: \TURBOC(对于PATH) 或YES(对于87)。
以下提供一样例程序EXAMPLE.EXE, 演示如何在main()函数中使用三个参数:/*program name margc.EXE*/#include <stdio.h>#include <stdlib.h>void main(int argc, char *argv[], char *env[]){int i;printf("These are the %d command- line argumentspassed to main:\n\n", argc);for(i=0; i<argc; i++)printf("argv[%d]:%s\n", i, argv[i]);printf("\nThe environment string(s)on this systemare:\n\n");for(i=0; env[i]!=NULL; i++)printf(" env[%d]:%s\n", i, env[i]);}运行结果:。
c语言中int main的含义
c语言中int main的含义C语言中的intmain()函数是程序的入口点,也是程序中最重要的部分之一。
在程序运行时,操作系统会首先调用main函数来执行程序的初始化工作,然后再根据程序的逻辑执行其他的函数和语句。
因此,对于C语言的初学者来说,理解int main()函数的含义和用法是非常重要的。
一、int main()函数的定义在C语言中,每个程序都必须包含一个int main()函数,这个函数是程序的入口点,也是程序中最重要的部分之一。
int main()函数的定义如下:int main(){//程序的主要逻辑代码return 0;}在上面的代码中,int main()是函数的定义,它告诉编译器这个函数的返回值是一个整型数。
函数体中的代码是程序的主要逻辑代码,它会在程序运行时被执行。
最后一行的return 0;是函数的返回语句,它告诉操作系统程序的执行结果是0。
二、int main()函数的作用int main()函数的作用是执行程序的初始化工作,并且根据程序的逻辑执行其他的函数和语句。
具体来说,int main()函数的作用包括以下几个方面:1. 程序的初始化工作在程序运行时,操作系统会首先调用int main()函数来执行程序的初始化工作。
这些工作包括设置程序的环境变量、初始化全局变量、打开文件等等。
只有当程序的初始化工作完成后,才能开始执行其他的函数和语句。
2. 程序的主要逻辑代码int main()函数中的代码是程序的主要逻辑代码,它会根据程序的逻辑执行其他的函数和语句。
具体来说,程序的主要逻辑代码可以包括输入输出、计算、判断、循环等等。
在程序运行时,这些代码会被执行,从而实现程序的功能。
3. 程序的返回值int main()函数的返回值是一个整型数,它表示程序的执行结果。
通常情况下,返回值为0表示程序执行成功,返回值为非零数表示程序执行失败。
在程序运行时,操作系统会根据int main()函数的返回值来判断程序的执行结果,并且根据需要采取相应的措施。
C语言main()函数详解
C语言main()函数详解C的设计原则是把函数作为程序的构成模块。
main()函数称之为主函数,一个C程序总是从main()函数开始执行的。
一、main()函数的形式在最新的 C99 标准中,只有以下两种定义方式是正确的:int main( void ) /* 无参数形式 */{...return 0;}int main( int argc, char *argv[] ) /* 带参数形式 */{...return 0;}int指明了main()函数的返回类型,函数名后面的圆括号一般包含传递给函数的信息。
void表示没有给函数传递参数。
关于带参数的形式,我们等会讨论。
浏览老版本的C代码,将会发现程序常常以。
main()这种形式开始。
C90标准允许这种形式,但是C99标准不允许。
因此即使你当前的编译器允许,也不要这么写。
你还可能看到过另一种形式。
void main()有些编译器允许这种形式,但是还没有任何标准考虑接受它。
C++ 之父Bjarne Stroustrup 在他的主页上的FAQ 中明确地表示:void main( ) 的定义从来就不存在于 C++ 或者 C 。
所以,编译器不必接受这种形式,并且很多编译器也不允许这么写。
坚持使用标准的意义在于:当你把程序从一个编译器移到另一个编译器时,照样能正常运行。
二、main()函数的返回值从前面我们知道main()函数的返回值类型是int型的,而程序最后的 return 0; 正与之遥相呼应,0就是main()函数的返回值。
那么这个0返回到那里呢?返回给操作系统,表示程序正常退出。
因为return语句通常写在程序的最后,不管返回什么值,只要到达这一步,说明程序已经运行完毕。
而return的作用不仅在于返回一个值,还在于结束函数。
现在我们来做一个小试验(注意:本人的系统是Windows XP, 编译环境是TC)来观察main()函数的返回值。
编写如下代码并编译运行://a.c#include "stdio.h"int main(void){printf("I love you.");return 0;}将这个文件保存为a.c,编译运行后会生成一个a.exe文件。
嵌入式--main函数是一个什么样的函数
(2)如果函数指定除 void 之外的其他返回类型,那么必须在函数中加入一 条 return 语句。
1.2、main 函数的返回值
我们知道 main 函数是特殊的,因为 C 语言规定了 main 函数是整个程序的入
口,其它的函数只有直接或间接被 main 函数调用才能被执行。C 语言的设计原
Hale Waihona Puke 则就是把函数作为程序的构成模块,main 函数就像是我们搭积木的主体,函数
摘取自【朱有鹏 c 语言内核深度解析】
2、argc、argv 与 main 函数的传参
从前面的知识,我们知道 main()函数的参数是由调用 main 函数所在的程 序的父进程专递的,并且它接收 main 的返回值。在 linux C 中,main()函数 可以传参,也可以不传参,int main(void)这种形式就表示我们没有给 main 传 参。但有时候我们希望程序具有一定的灵活性,所以选择在执行程序时通过传参 来控制程序的运行,达到不需要重新编译程序就可以改变程序运行结果的效果。 主函数 main()的第一个参数是命令行中的字符串个数,即程序运行的时候给 main()函数传递的参数个数。
printf("第%d 个参数是%s.\n", i, argv[i]); } return 0; }
编译,在命令行中输入 ./a.out 0 回车,程序运行的结果如下:
main 函数传参个数是:2. 第 0 个参数是./a.out. 第 1 个参数是 0.
在 main 传参我们需要注意一下几点,否侧容易出现问题。 1.main()函数传参都是通过字符串传进去的。 2.main()函数只有被调用时传参,各个参数(字符串)之间是通过空格来间隔的。 3.在程序内部如果要使用 argv,那么一定要先检验 argc。
MDK4选项说明
MDK4选项说明Keil µVision 4 ⽬标⼯具选项详解⼀、⽬标⼯具选项(Target Options …)µVision可以设置⽬标硬件的选项。
通过下图1中的⼯具栏2区域按钮或1区域菜单项P roject -> Options for Target打开Options for Target对话框。
图1⼆、设备选项卡(device )在 Target页中设置⽬标硬件及所选CPU⽚上组件的参数。
如图2所⽰。
图2在图2中,各区域说明如下:1:显⽰当前选定的CPU的⼚商名,CPU的型号,和编译⼯具。
2:是该cpu 的⼀些描述。
3:更改所使⽤的CPU,三、硬件⽬标设置选项卡(Target),见图3 所⽰。
图3在图3中,各区或详细说明如下:1:指定⽤晶振频率,可以⽤于模拟调试,仅反映外部振荡频率。
2:可以选择KEIL集成的实时操作系统RTX Kernel。
针对复杂的嵌⼊式应⽤,MDK内部集成了由ARM开发的实时操作系统(RTOS)内核RTX,它可以帮助⽤户解决多时序安排、任务调度、定时等⼯作。
值得⼀提的是,RTX可以⽆缝集成到MDK⼯具中,是⼀款需要授权的、⽆版税的RTOS。
RTX程序采⽤标准C语⾔编写,由RVCT编译器进⾏编译。
4:⽚外ROM设置,最多⽀持3块ROM(Flash),在Start栏输⼊起始地址,在Size栏输⼊⼤⼩。
若是有多⽚⽚外ROM,需要在7区域设置⼀个作为启动存储块,程序从该块启动;有⼏块ROM需要选中对应的3区域。
6:⽚内ROM设置。
设置⽅法同⽚外ROM,只是程序的存储区在芯⽚内集成。
9:使⽤ Cross-Module优化。
10:使⽤MicroLib库。
它旨在与需要装⼊到极少量内存中的深层嵌⼊式应⽤程序配合使⽤.这些应⽤程序不在操作系统中运⾏. MicroLib进⾏了⾼度优化以使代码变得很⼩. 它的功能⽐缺省 C 库少,并且根本不具备某些ISO C 特性. 某些库函数的运⾏速度也⽐较慢,例如,memcpy(). 与缺省 C 库之间的差异MicroLib与缺省 C 库之间的主要差异是: MicroLib不符合 ISO C 库标准. 不⽀持某些 ISO 特性, 并且其他特性具有的功能也较少. MicroLib不符合 IEEE 754 ⼆进制浮点算法标准. MicroLib进⾏了⾼度优化以使代码变得很⼩. ⽆法对区域设置进⾏配置. 缺省 C 区域设置是唯⼀可⽤的区域设置. 不能将main() 声明为使⽤参数,并且不能返回内容. 不⽀持stdio,但未缓冲的stdin,stdout 和 stderr 除外. MicroLib对 C99 函数提供有限的⽀持. MicroLib不⽀持操作系统函数. MicroLib不⽀持与位置⽆关的代码. MicroLib不提供互斥锁来防⽌⾮线程安全的代码. MicroLib不⽀持宽字符或多字节字符串. 与 stdlib 不同, MicroLib 不⽀持可选择的单或双区内存模型. MicroLib只提供双区内存模型,即单独的堆栈和堆区. 可以合理地将MicroLib与 --fpmode=std 或 --fpmode=fast 配合使⽤. MicroLib 中的函数负责: 创建⼀个可在其中执⾏ C 程序的环境. 这包括: 创建⼀个堆栈创建⼀个堆(如果需要) 初始化程序所⽤的库的部分组成内容.11:指令集中也分为⾼字节结尾,⼤端模式。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
图 2-10 MAP 文件分析 0x0800 015D 地址是函数_scatterload_copy 的入口,该函数到底 copy 了什么 值呢?在此之前我们先要熟悉一下.map 文件 .map 文件是值包括了映像文件信息图和其它信息的一个映射文件, 该文件包 含了: (1) 从映像文件中删除的输入段中未使用段的统计信息,对应参数-remove; (2) 域符号映射和全局、局部符号及生成符号映射统计信息,对应参数 -symbol; (3) 映射文件的信息图,对应参数-map,该信息中包含映像文件中的每个加载 域、运行域和输入段的大小和地址,如 2-11 图、2-12 图所示:
图 2-19
经 过 该 函 数 处 理 得 : r0=0x2000 0098,r1=0x2000 0698,r2=0x2000 0298,r3=0x2000 0298。最后用户栈顶被设置成 0x2000 0698,完成了堆栈的初始 化工作,程序返回到 rt_entry_main
三、 总结
最终函数终于跳转到我们的 main 函数执行我们写的代码。 总结启动文件的整个过程,分为如下: (1) 系统初始化,包括对中断向量表的重新映射; (2) 加载 RW 段; (3) ZI 段清零; (4) 初始化用户堆栈; (5) 初始化微库(具体干什么我也不知道,屏蔽此处函数好像也能正常运行) ; (6) 调用 main 函数。
Main 函数运行前的分析(原创,转载请注明出处)
一、 启动文件的介绍
在 MDK 的启动文件 startup_stm32f10x_md_vl 中,该文件分别定义了栈段、 堆段、存放中断向量表的数据段、还有一个代码段 大小为 0x400 的栈段定义如图 1-1:
图 1-1 大小为 0x200 的堆段如图 1-2:
图 2-15
_scatterload_copy 函数分析 经过_scatter_null 函数我们得到 r2=0x00000034, 这正是需要初始化全局变量 的大小,由此我们猜测 copy 的值是全局变量。 第一条指令是得 r2=0x0000 0024,然后判断标志位 C 是否为 1,如果是 1,则 执行下面两条语句: LDM r0! , (r3-r6); STM r1! , (r3-r6); r0=0x0800 2040, r1=0x2000 0000,而 0x20000000 正好是 SRAM 的起始地址, 所以 上面两条语句是把以 0x8002040 为起始的地址复制到以 0x2000 0000 为起始的地 址,该循环类似我们的 for(r0=0x0800 2040, r1=0x2000 0000;r2=r2-0x10;r2>0), 直到 r2 为负数 LSLS r2,r2,#29 ITT CS 如果 r2 除以 16 是 8 的整数,则复制该 8 字节 LDM r0!,{r4-r5} STM r1!,{r4-r5} ITT MI 如果 r2 除以 16 是 4 的整数,则复制 4 或者 12 字节 LDR r4,[r0,#0x00] STR r4,[r1,#0x00] 这样_scatterload_copy 完成了需要初始化全局变量 RW 的装载过程,最后函 数返回到_scatterload_null。 _scatterload_zeroinit 函数分析 然后判断 r10 是否与 r11 相等,很明显不等,r3=0x0800 0178,函数跳转,进入 到_scatterload_zeroinit 函数,此时 r0=0x0800 2074,r1=0x20000034, r2=0x0000 0664 _scatterload_zeroinit 函数代码如图 2-16:
图 1-2 由其定义属性可知,栈和堆都未初始化,该过程由后面的_user_initial_stackheap 来完成。 存放中断向量的数据段,如图 1-3 所示:
图 1-3 10 个系统异常过程段和在同一地址的外部中断过程段, 下面我们就详细介绍 上电复位的代码段,如图 1-4 所示:
图 1-4
二、Reset_Handler 段分析
图 2-4 该函数首先保存跳转前的有关状态,然后根据使用的芯片,进行相应的初始化操 作,函数最后重新映射了中断向量的存放地址,如图 2-5:
图 2-5
查看反汇编窗口,如图 2-6:
图 2-6 根据分析可知,该段代码是把 0x0800 0000 存放到地址为 0xE000 ED08 处, 查找 Cortex_M3 手册如图 2-7 所示,该地址是向量表偏移寄存器,也就是说,这 条语句把中断向量表重新映射到地址为 0x0800 0000。
图 2-11
图 2-12 (4) 映像文件的每个输入文件或库的 RO、 RW、 ZI 等统计信息, 对应参数-info sizes,示例如图 2-13:
图 2-13 (5) 文件对象类和库总的 RO、RW、ZI 等下大小,对应参数-info totals,示例 如图 2-14:
图 2-14 由此,根据.map 文件我们得到 RW=0x34, ZI=0x0644, Code+RO=0x2040, RW+ZI=0x0698 现在我们回到_scatterload_copy 中,看看到底 copy 了什么,其代码如图 2-15 所示:
图 2-18 _user_setup_stackheap 函数的第一条指令是保存函数的返回地址,此处为 什么没有用 PUSH ?因为此时堆栈还没有初始化好。第二条指令是跳转到 _user_libspace 进行一些微库的初始化工作,后面的几条语句是建立一个大小为 90 字节的临时栈,然后程序跳转到_user_inital_stackheap 进行用户栈的初始化, 这也就是启动文件 startup_stm32f10x_md_vl 中初始化堆栈段的那些语句,如图 2-19
1. _systeminit 函数分析
STM32 上电启动, 首先从 0x0000 0000 处初始化 sp 的值, 然后从 0x0000 0004 处取得复位中断处理的地址 0x0800 1F6D,程序跳转
图 2-1 但是 Reset_Handler 的地址为 0x0800 1F6C, 这是因为 Cortex-M3 使用的是 thumb-2 指令集,其最低位必须为 1;如果为 0,则会出现异常,如图 2-1 所示。 查看反汇编窗口,如图 2-2 所示:
图 2-9 查看.map 文件,知道 0x08002020 称为 Region$$Table$$Base,0x08002040 称为 Region$$Table$$Limit。 _scatterload_null 函数分析 如图 2-10 所示,程序继续执行到_scatterload_null 函数,首先比较 r10、r11 是否相等,如果不等则跳转到 0x0800013E。很明显不等,程序跳转,第一条指 令是把 0x08000137f 赋值给 lr,其实就是保存_scatterload_null 的入口地址;第二 条指令是把 r10=0x08002020 为起始,0x08002030 为终止的地址内容分别赋值给 r0-r3,最后我们得到 r0=0x08002040, r1=0x20000000, r2=0x00000034, r3=0x0800 015C,r10=0x08002030;第三条指令是判断 r3 是否为 1,很明显不为 1,IT 指令 等效于 if-then 模式,最后几条指令可以得到 r3=0x0800 015D,程序跳转
程序最后返回到_scatterload,接着跳转到_rt_entry,如图 2-17
图 2-17 2. _rt_entry 函数分析 _rt_entry 的 第 一 条 指 令 又 是 一 条 跳 转 指 令 , 程 序 再 次 跳 转 到 _user_setup_stackheap,如图 2-18
图 2-7
2. _main 函数分析
进行完相应的初始化,函数跳转到_main 函数, _main 函数的入口地址从 0x08001F98 取得,通过查找,发现 _main 函数的地址为 0x08000121, 跳转到 0x0800120,证明此处就是我们要找的_main 函数入口,如图 2-8
图 2-8 _main 函数到底进行了哪些操作呢,下面我们就一一逐步分析过去。 _scatterload 函数分析 首先是一条跳转指令,跳转到_scatterload 函数,该函数的第一条指令是把地 址 0x08000154 赋值给 r0(但是此处 PC+4 取得值应该是 0x0800012C,为什么会 是 0x08000154 呢?根据 ARMv7-M Architecture Reference Manual,ADR 的二进 制码, 相差 0x28) , 第二条指令是分别把 0x08000154、 0x08000158 存放的 0x0000 1ECC、0x0000 1EEC 给 r10、r11,如图 2-9。经过该函数的第三、第四条指令, 我们可以得到 r10=0x08002020,r11=0x08002040,r7=0x08uanzhipingysu2011@,谢谢!
图 2-16 通过_scatterload_copy 我们可以猜测_scatterload_zeroinit 是一个清零过程, 但 是对什么需要清零呢?当然是 ZI 段,由 r2=0x00000664 这正是 ZI 的大小,所以 该过程是以 0x20000034 为起始地址,大小为 r2=0x00000664 的清零过程,具体 分析和_scatterload_copy 类似,不再重复。
图 2-2 可以知道先取得 SystemInit 函数的地址,该地址是多少,我们可以跳转到 0x08001F94 看看,如下所示,该地址保存了值为 0x0800045D 的数,如图 2-3:
图 2-3 然后我们跳转到 0x0800045D,发现该处正是我们需要的 SystemInit 函数入口地 址,如图 2-4: