程序结束后去哪儿了?

大家好,我是写代码的篮球球痴,转发一篇卓老师的文章,文章中的内容我之前做单片机的时候也有遇到过。

推荐给大家看看~


faf9fb9c3e2df77bd95ea3da16355481.png

简 介: 对于嵌入式系统,如果没有运行RTOS,那么程序开发中的 主函数(main())需要通过某种机制使其永远愉快的运行下去,它没有终点。如果想从main函数中退出,具体干什么是由所使用的C语言编译器决定的。
关键词C51main程序退出

01 问题提出

  今天在CSDN的  单片机led模块定义函数的问题[1]  中看到一个有趣的问题。提问者在进行基本的C51编程实验,编写了一个简单的C51程序如下:

#include <REGX51.H>void test(num) {switch(num) {case 1: P2_0=0; P2_1=0; break;}
}void main(void) {test(1);
}

  程序执行完之后,可以看到实验板上的有两个LED被点亮,另外六个居然微微发亮

9ebb522e554722d8b0ea54559ead6b5b.png
▲ 图1.1 实验板上的未点亮的LED居然微微发亮

  如果在主程序中,增加一个无限循环:while(1); ,则电路板上的就不再会出现“微微点亮”的现象了。

#include <REGX51.H>void test(num) {switch(num) {case 1: P2_0=0; P2_1=0; break;}
}void main(void) {test(1);while(1);
}
bea3286d72a9707924a402c2271b1833.png
▲ 图1.2 实验板上后面六个LED就不再点亮了

  上面两种情况的区别,在于第二个程序中 主循环 main() 函数始终没有退出,而第一个程序,main() 函数退出了。似乎前面LED 微微点亮 应该与 主函数 退出之后,单片机都干了些啥有关系。

  那么就剩下一个问题:对于普通的嵌入式系统,C语言编程中 main()函数退出之后,程序去哪儿了?

02 程序去哪儿了?

  从上面提问者书写的代码来看,应该是一位C51的爱好者,使用的是C51的编译器,在一款C51开发板上愉快的进行实验。他一开始没有安装嵌入式程序开发的惯例 在主程序void main(void) 中利用无限循环将程序控制在主程序函数中,就出现了前面实验结果中令人迷惑的情况。

注:他是一个胆大心细的人,观察还挺仔细的。

2.1 盘古开天辟地

  对于C语言编程来说,所有的用户程序世界是从主程序 main()  开始的。给用户程序开天辟地的任务是由 一小段 盘古代码 STARTUP.A51

  关于C51是如何启动的, 在如下面博文中也被测试说明:

  • 51单片机程序执行流程(STARTUP.A51管理Main函数的执行)[2]

  下面截取了 STARTUP.A51 代码的一段,可以看到盘古在单片机 RESET 之后做了点准备工作(初始化全局变量、堆栈指针)之后,就直接跳转至:?C_START

NAME    ?C_STARTUP?C_C51STARTUP   SEGMENT   CODE
?STACK          SEGMENT   IDATARSEG    ?STACKDS      1EXTRN CODE (?C_START)PUBLIC  ?C_STARTUPCSEG    AT      0
?C_STARTUP:     LJMP    STARTUP1RSEG    ?C_C51STARTUPSTARTUP1:IF IDATALEN <> 0MOV     R0,#IDATALEN - 1CLR     A
IDATALOOP:      MOV     @R0,ADJNZ    R0,IDATALOOP
ENDIFIF XDATALEN <> 0MOV     DPTR,#XDATASTARTMOV     R7,#LOW (XDATALEN)IF (LOW (XDATALEN)) <> 0MOV     R6,#(HIGH (XDATALEN)) +1ELSEMOV     R6,#HIGH (XDATALEN)ENDIFCLR     A
XDATALOOP:      MOVX    @DPTR,AINC     DPTRDJNZ    R7,XDATALOOPDJNZ    R6,XDATALOOP
ENDIFIF PPAGEENABLE <> 0MOV     PPAGE_SFR,#PPAGE
ENDIFIF PDATALEN <> 0MOV     R0,#LOW (PDATASTART)MOV     R7,#LOW (PDATALEN)CLR     A
PDATALOOP:      MOVX    @R0,AINC     R0DJNZ    R7,PDATALOOP
ENDIFIF IBPSTACK <> 0
EXTRN DATA (?C_IBP)MOV     ?C_IBP,#LOW IBPSTACKTOP
ENDIFIF XBPSTACK <> 0
EXTRN DATA (?C_XBP)MOV     ?C_XBP,#HIGH XBPSTACKTOPMOV     ?C_XBP+1,#LOW XBPSTACKTOP
ENDIFIF PBPSTACK <> 0
EXTRN DATA (?C_PBP)MOV     ?C_PBP,#LOW PBPSTACKTOP
ENDIFMOV     SP,#?STACK-1LJMP    ?C_STARTEND

  上面的代码也被博文  51单片机程序执行流程(STARTUP.A51)[3] 中进行逐步调试跟踪验证过:

64a110f5439ba6fd9729aed898f25748.png
▲ 图2.1.1  显示LJMP C_START 就是进入 main() 程序

2.2 世界尽头

  由于进入main() 函数是长跳转,所以main函数是不会正常返回到启动程序 STARTUP.A51,那么程序去哪了?

  在博文 单片机C语言while(1)的问题 中作者对于 KEIL编译器和PIC的 MAPLAB编译器对于main函数的最后时光进行了反汇编查看。

2.2.1 Keil编译器

  在main函数的最后,程序增加了一下几行代码:

MOV R0, #0x7F
CLR A
MOV @R0, A
DJNZ R0, (3)
MOV SP, #0x0C
LJMP main

  这几条语句,前4条,是将我们单片机的内存的前128个地址清零,第5条,是定义堆栈,第6条,是将程序重新跳转到main函数的首行进行执行。

2.2.2 MAPLAB编译器

  PIC 单片机语言程序进行跟踪,发现main() 函数最后一条语句为 reset,也就是单片机直接复位,这是 MAPLAB编译器根据 PIC 单片机特点增加的复位语句。

※ 总  结 ※

  对于嵌入式系统,如果没有运行RTOS,那么程序开发中的 主函数(main())需要通过某种机制使其永远愉快的运行下去,它没有终点。

  如果想从main函数中退出,具体干什么是由所使用的C语言编译器决定的。

参考资料

[1]

单片机led模块定义函数的问题: https://ask.csdn.net/questions/7640604?utm_medium=distribute.pc_feed_v2.none-task-ask-ask_personrec_tag-3.pc_personrecdepth_1-utm_source=distribute.pc_feed_v2.none-task-ask-ask_personrec_tag-3.pc_personrec

[2]

51单片机程序执行流程(STARTUP.A51管理Main函数的执行): https://blog.csdn.net/ChenGuiGan/article/details/88769619

[3]

51单片机程序执行流程(STARTUP.A51): https://blog.csdn.net/tangsun999/article/details/45604507

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/464533.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CodeSmith终极玩法

CodeSmith是一个模仿asp.net运行机制的代码生成器, 运行时分析模板(相当aspx文件)的预编译指令和主体内容, 生成一个继承自CodeSmith.Engine.CodeTemplate(相当于System.Web.UI.Page)或者一个在Inherites预编译指令指定的类(相当于CodeBehind类)的源码, 且把它编译. 然后把这个…

bom与dom

区别 BOM&#xff08;Browser Object Model&#xff09; BOM 即浏览器对象模型&#xff0c;BOM没有相关标准&#xff0c;BOM的最核心对象是window对象。window对象既为javascript访问浏览器提供API&#xff0c;同时在ECMAScript中充当Global对象。BOM和浏览器关系密切&#xff…

Service 的生命周期;两种启动方法和区别

1&#xff1a;startService Service的生命周期&#xff1a;onCreate() --> onStart() -> onDestroy() 停止服务&#xff1a;service.stopService() 2&#xff1a;bindService Service的生命周期 onCreate() --> onBind() --> onUnBind() --> onDestroy() 停止…

用反射简化 asp.net 报表的一点总结

有几个报表, 查询条件都一样&#xff0c;仅仅里面GridView中有几个列区别&#xff0c;以前图快&#xff0c;就把原来Report.aspx文件拷贝一份&#xff0c;改名为Report1.aspx&#xff0c;然后&#xff0c;修改里面的column&#xff0c;然后再由后台库查出数据&#xff0c;填充到…

超轻型的数据库sqlite

很多次看到别人提到这个东西&#xff0c;最近因项目需要看了看&#xff0c;确实好用。总共就两个文件&#xff0c;我想不能再简单了。还是开源的。开发rails也可以用&#xff0c;不错&#xff01;转载于:https://www.cnblogs.com/chenge/archive/2005/06/29/183508.html

有些事不用听别人的

今天在群里跟几个朋友聊天&#xff0c;然后说了自己的想法&#xff0c;最近很多人在说公众号不会有好的发展&#xff0c;写文章也是没有出路的。不过这个是事实。短视频才是可能是出路&#xff0c;短视频是个很大的蛋糕&#xff0c;从表达方式上来说&#xff0c;视频的表达方式…

php读取三维数组,php 读取多维数组方法_PHP教程

php 读取多维数组方法for($i0;$i{for($j0;$j{echo $array[$j][userid];}}因为它是个多维数组,一般用foreach ($arr as $key > $value) {echo "Key: $key; Value: $value\n";}Array ( [0] > Array ( [userid] > 1 [username] > 刘振鹏 [status] > 0 [u…

互斥锁mutex的使用方法

在线程实际运行过程中&#xff0c;我们经常需要多个线程保持同步。这时可以用互斥锁来完成任务&#xff1b;互斥锁的使用过程中&#xff0c;主要有pthread_mutex_init&#xff0c;pthread_mutex_destory&#xff0c;pthread_mutex_lock&#xff0c;pthread_mutex_unlock这几个函…

1000瓶药水,1瓶有毒药,几只小白鼠能够找出毒药

1000瓶药水&#xff0c;1瓶有毒药&#xff0c;服用后一小时毒发&#xff0c;毒药可以无限稀释&#xff0c;那么一小时内用几只小白鼠能够找出毒药&#xff1f; 假如是8瓶药水&#xff0c;3只小白鼠。 0000  0011  0102  0113  1004  1015  1106  1117 每位数表示…

25个优秀的设计机构网站设计案例

今天&#xff0c;我们一起来欣赏网站设计工作室自己的网站。设计公司的网站除了要能够吸引客户以外&#xff0c;还要通过他们自己的网站向客户展示他们的设计理念和风格。这里收集的25个优秀的设计机构网站既有清爽简洁风格的&#xff0c;也有色彩丰富&#xff0c;图文并茂的&a…

windows server 2003 出错提示请求的资源在使用中解决方案

1.杀毒软件造成IIS站点‘请求的资源在使用中’”的消息&#xff0c;如瑞星2005&#xff0c;服务器上使用单机版杀毒一般都不会稳定的。马上把瑞星卸载了&#xff0c;重启机器&#xff0c;一切正常。 2.IUSR_机器名和IWAM_机器名的账号不同步,这个在网上已经有解决方案了只要打开…

一个适用各类场合的Makefile模板

1.写在前面对于Windows下开发&#xff0c;很多IDE都集成了编译器&#xff0c;如Visual Studio&#xff0c;提供了“一键编译”&#xff0c;编码完成后只需一个操作即可完成编译、链接、生成目标文件。Linux开发与Windows不同&#xff0c;Linux下一般用的的gcc/g编译器&#xff…

matlab r2014a错误,MATLAB中的潜在错误使R2014a回归

MATLAB R2014a过去工作得很好w回归但是现在当变量很好并且排名满意时我得到一个错误。X rand([10 3])X 0.8407 0.3517 0.07590.2543 0.8308 0.05400.8143 0.5853 0.53080.2435 0.5497 0.77920.9293 0.9172 0.93400.3500 0.2858 0.12990.1966 0.7572 0.56880.2511 0.7537 0.469…

Android 如何退出整个应用程序?

转载文章&#xff1a;http://blog.csdn.net/sunnyfans/article/details/7688092 Android 怎么退出整个应用程序&#xff1f; 我们在写android应用程序时&#xff0c;经常会遇到想退出当前Acitivity&#xff0c;或者直接退出应用程序.我之前的一般操作是按返回键&#xff0c;或者…

excel拼接数据宏

将sheet2的A2 和 G2 加上 sheet5的A2和B2合一起生成新的sheet--就是将两个sheet的指定列前后拼接一起作为一个新的sheet Sub addwork() Sheets.Add after:Sheets(Sheets.Count) ActiveSheet.Name "临时合并数据" Dim s, sht, shts Dim arr, brr1, br…

关于我曾经做过的一个商业社区的ui框架

1.主页面default2.容器,中间页面process载入模板3.模板:BasePage4,BasePageprotected override void Render(System.Web.UI.HtmlTextWriter writer) {writer.WriteLine("Header");base.Render(writer);writer.WriteLine("Header");}转载于:https://www.cnb…

毕业十年|我的嵌入式AI学习路线(笔记、代码)

嵌入式从业者接下来会有怎样的黄金十年&#xff1f;在物联网和人工智能的促进下&#xff0c;嵌入式在未来的5-10年内会迎来更多的发展机会&#xff0c;一方面嵌入式开发会迎来更多的应用场景&#xff0c;另一方面嵌入式开发的技术体系也会逐渐丰富&#xff0c;从而拓展物联网开…

DAL调用SP时出现的异常处理

三种情形&#xff1a; 1.执行SP时出错&#xff0c;如连接异常&#xff0c;数据库权限异常 2.SP代码中抛出异常 3.返回值/输出参数的值不符合约定 后两种情形需要在异常发生时记录传入SP的参数值&#xff1b;业务逻辑层需要捕获并重抛&#xff0c;以保证UI层提供给用户友好的错误…

在艰苦年代,买不起万用表,怎么测量电路电压?

如何测量电压&#xff1f;有这样一张图片&#xff1a;用舌头来测量&#xff1f;开玩笑的吧&#xff01;不过这张照片勾起了我的回忆&#xff1a;有一位玩电子的老前辈&#xff0c;现在已经70多岁了。和他聊天&#xff0c;他说&#xff0c;当年他们玩电子&#xff0c;条件非常艰…

71道经典Android面试题,涵盖了所有android知识点,值得学习和思考

1. 下列哪些语句关于内存回收的说明是正确的? (b ) A、 程序员必须创建一个线程来释放内存 B、内存回收程序负责释放无用内存 C、内存回收程序允许程序员直接释放内存 D、内存回收程序可以在指定的时间释放内存对象 2. 下面异常是属于Runtime Excepti…