在51单片机上使用递归的注意事项

目录

  • 问题
  • 应对措施
  • 原理

普中51-单核-A2
STC89C52
Keil uVision V5.29.0.0
PK51 Prof.Developers Kit Version:9.60.0.0


问题

       在Keil C51中直接使用递归会报如下警告:

       recursive call to non-reentrant function

       为了提高运行效率,C51采用静态分配局部变量的方式,所以一般情况下不可递归。
被中断和非中断函数调用的函数,如果在非中断状态运行,发生中断后,局部变量被破坏,中断结束后再执行就完全错误了,这个跟不能递归的原理是一样的。
如对数组arr[10] = { 7,1,3,9,2,6,1,7,3,5 }进行快速排序

#include <REGX52.H>
#include "intrins.h"
#include "stdint.h"
#include "UART.h"void Delay500ms()		//@22.1184MHz
{unsigned char i, j, k;_nop_();i = 8;j = 1;k = 243;do{do{while (--k);} while (--j);} while (--i);
}typedef int QuickSortType;
void QuickSort(QuickSortType v[], int left, int right) 
{int i, pos;QuickSortType temp;if (left >= right)return;//取第一个元素为锚定点pos = left;//遍历数组,将小于锚定点的数swap到数组前部for (i = left + 1; i <= right; ++i) {if (v[i] < v[left]){ ++pos;temp = v[i];v[i] = v[pos];v[pos] = temp;}}//将锚定点的数与swap至其正确位置pos;temp = v[left];v[left] = v[pos];v[pos] = temp;//递归该过程QuickSort(v, left, pos - 1);QuickSort(v, pos + 1, right);
}void main(void)
{int i;int arr[10] = { 7,1,3,9,2,6,1,7,3,5 };UART_Init(57600);QuickSort(arr, 0, 9);while(1){Delay500ms();for(i = 0; i < 10; ++i)printf("%d ", arr[i]);printf("\r\n");}
}

得到的数据可能会出错
在这里插入图片描述

应对措施

加上reentrant关键字后,输出结果正确。

typedef int QuickSortType;
void QuickSort(QuickSortType v[], int left, int right) reentrant
{int i, pos;QuickSortType temp;if (left >= right)return;//取第一个元素为锚定点pos = left;//遍历数组,将小于锚定点的数swap到数组前部for (i = left + 1; i <= right; ++i) {if (v[i] < v[left]){ ++pos;temp = v[i];v[i] = v[pos];v[pos] = temp;}}//将锚定点的数与swap至其正确位置pos;temp = v[left];v[left] = v[pos];v[pos] = temp;//递归该过程QuickSort(v, left, pos - 1);QuickSort(v, pos + 1, right);
}

在这里插入图片描述
reentrant后也不能随意使用递归:
由于51稀缺的资源,当数组长度达到46后也出现了错误,因此在51上尽量不要使用递归
在这里插入图片描述
在这里插入图片描述

原理

本节摘自Keil C51对C语言的关键词扩展之十五: reentrant —— 昵称90天可改
这篇博文写的很详细模拟堆栈,可重入函数调用,参数传递 —— hplog

       reentrant声明的函数为可重入函数。可重入的函数能够被多个进程同时调用。可重入函数在执行时,另外的进程可以中断当前执行的函数,并且调用同一个函数。正常情况下,C51程序中的函数不能被递归地调用,这是由于函数的参数和局部变量都被保存在固定的地址,在递归调用时操作了相同存储位置,导致数据被覆盖。

       使用reentrant声明函数为可递归调用的可重入函数:

int calc (char i, int b) reentrant  
{int  x;x = table [i];return (x * b);
}

       可重入函数,能够被递归调用,也能被两个以上的进程同时调用。可重入函数通常在实时应用或者中断与非中断程序共享相同函数这两种情况下被使用。
每个可重入函数都有一个位于内部ram或外部ram的模拟堆栈:

  1. SMALL内存模型下,可重入函数模拟堆栈位于idata区;

  2. COMPACT内存模型下,可重入函数模拟堆栈位于pdata区;

  3. LARGE内存模型下,可重入函数模拟堆栈位于xdata区;

使用reentrant声明可重入函数须遵循的规则:

  1. 可重入函数不支持位寻址变量,比如bit类型的参数;

  2. 可重入函数不能被alien函数调用;

  3. 可重入函数不能被声明为alien属性(alien用于使能PL/M-51参数传递约定);

  4. 可重入函数可以同时拥有其他属性,比如using、interrupt、small、compact、large;

  5. 返回地址被保存在硬件堆栈中;

  6. 使用不同存储模型的可重入函数能够混合,但是各自函数声明时必须指定存储模型;

  7. 三种内存模型的可重入函数都有自己的对战区和栈指针。比如在相同模块中,定义了small和large类型的可重入函数,则small和large类型的堆栈及其堆栈指针都被创建;

       可重入堆栈模拟体系,效率比较低下,但是由于8051自身缺乏适当寻址方法的硬件特性,所以推出这种堆栈模拟体系来满足我们的可重入需求,在应用中 ,我们应该尽量不用或少用可重入函数。

       可重入函数使用的模拟堆栈拥有独立于8051硬件堆栈的栈指针。堆栈和堆栈指针在STARTUP.A51文件中被定义和初始化。

       8051硬件堆栈向上增长,堆栈指针先加再压栈,可重入模拟堆栈正好相反,其指针先减再压栈。
       STARTUP.A51启动代码声明并初始化了模拟堆栈及其堆栈指针,如果使用可       重入函数就必须修改启动代码指出哪个模拟堆栈须要初始化。可以在启动代码中修改模拟堆栈的起始地址。
       可重入函数的参数传递,通过模拟堆栈的压栈、出栈完成。
       可重入函数的局部变量,也保存在模拟堆栈中,通过模拟堆栈指针访问。

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

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

相关文章

C盘瘦身:QQ文件的清理及Group2文件夹

目录问题解决方法Windows 10 20H2 TIM 问题 最近C盘被撑爆了 使用SpaceSniffer一扫发现QQ的文件中有个Group2文件夹占了我17G 但使用QQ自带的个人文件夹清理却扫不到&#xff0c;据说直接删除会丢失近期所有群聊的聊天图片 解决方法 在这个地方找到了大神fsz1987给出的解…

获取ArcGIS安装路径

在要素类进行符号化时&#xff0c;使用axSymbologyControl需要安装路径下的Style文件路径&#xff0c;在AE9.3VS2008中是这样的&#xff1a; Microsoft.Win32.RegistryKey regKey Microsoft.Win32.Registry.LocalMachine.OpenSubKey("SOFTWARE\\ESRI\\CoreRuntime",…

【51单片机快速入门指南】3.2:定时器/计数器

目录快速使用硬知识传统51单片机 CPU 时序的有关知识&#xff08;12T&#xff09;51 单片机定时器原理51 单片机定时/计数器结构定时器/计数器0/1定时器/计数器0和1的相关寄存器控制寄存器工作模式寄存器工作模式模式0(13位定时器/计数器)模式1(16位定时器/计数器)模式2(8位自动…

sublime_text 3 注册序列号

为什么80%的码农都做不了架构师&#xff1f;>>> ----- BEGIN LICENSE ---- Andrew Weber Single User License EA7E-855605 813A03DD 5E4AD9E6 6C0EEB94 BC99798F 942194A6 02396E98 E62C9979 4BB979FE 91424C9D A45400BF F6747D88 2FB88078 90F5CC94 1CDC92DC 845…

【51单片机快速入门指南】3.2.1:PWM、呼吸灯与舵机

目录硬知识PWM&#xff08;脉冲宽度调制&#xff09;基本原理脉宽调制分类上机实战呼吸灯main.c中断服务函数修改TIM.c中的中断服务函数效果开发板电路分析舵机控制舵机控制方法main.c中断服务函数修改中断服务函数舵机测试程序main.c效果普中51-单核-A2 STC89C52 Keil uVisio…

Proteus仿真单片机:51单片机的仿真

目录新建工程调试在Proteus中编写程序导入Keil生成的Hex程序Windows 10 20H2 Proteus 8 Frofessional v8.9 SP2 Keil uVision V5.29.0.0 PK51 Prof.Developers Kit Version:9.60.0.0 新建工程 设置名称和路径 下一步 下一步 选择系列、控制器和编译器 双击MCU设置主频 …

Linux 命令行输入

这几天刚刚接触到Linux&#xff0c;在windows上安装的VMWare虚拟机&#xff0c;Centos7。安装什么都是贾爷和办公室的同事帮忙搞定的。 在虚拟机界面&#xff0c;按快捷键CtrlAltEnter&#xff0c;可以全屏显示Linux界面&#xff0c;再按一次则退出全屏。 如何在Linux里输入命令…

【51单片机快速入门指南】2.5:并行I/O扩展与8255A

目录硬知识单片机I/O扩展基础知识I/O接口电路的功能速度协调输出数据锁存数据总线隔离数据转换增强驱动能力单片机并行扩展总线并行扩展总线的组成80C51单片机并行扩展总线I/O编址技术可编程并行接口芯片82558255硬件逻辑结构口电路总线接口电路A组和B组控制电路中断控制电路82…

win 下 apache2.4 +tomcat7 集群

为什么80%的码农都做不了架构师&#xff1f;>>> 反正每次来做一个不熟悉的东西&#xff0c;就是各种的search ,前一次去做过一个apache的东西&#xff0c;各种蛋疼&#xff0c;各种不能用。好多的东西也是比较旧了的咯。 这次结合前辈的各种东借西拿&#xff0c;总…

Proteus仿真单片机:PIC18单片机的仿真

目录新建工程ProteusMPLAB X IDE调试Windows 10 20H2 Proteus 8 Frofessional v8.9 SP2 MPLAB X IDE v5.45 新建工程 Proteus 下一步 下一步 选择芯片、编译器 搭建实验电路 MPLAB X IDE MPLAB X IDE 新建工程 选择独有项目 选择芯片 选择编译器 配置工程名称、路…

Realm学习总结

参考博客: http://www.jianshu.com/p/096bec929f2a http://www.cnblogs.com/ilyy/p/5648051.html 参考的博客介绍很详细,我就不写了..写了一个简单的学习的demo. GitHub地址: https://github.com/PengSiSi/RealmDemo 代码如下: // // ViewController.m // RealmDemo // // C…

with(nolock)的用法

with(nolock)的介绍 大家在写查询时,为了性能,往往会在表后面加一个nolock,或者是with(nolock),其目的就是查询是不锁定表,从而达到提高查询速度的目的。 当同一时间有多个用户访问同一资源,并发用户中如果有用户对资源做了修改&#xff0c;此时就会对其它用户产生某些不利的影…

【PIC18单片机学习笔记】一、程序的烧录

目录编程器烧录软件烧录准备程序main.cpic18.h烧录效果Windows 10 20H2 PICkit 3 v3.10 MPLAB X IDE v5.45 PIC18F46J50 编程器 所用编程器为PICkit 3.5 按图示连接好编程器和开发板 烧录软件 所用烧录软件为PICkit 3 v3.10 初次使用需要给编程器更新固件&#xff0c…

ASIHttpRequest:创建队列、下载请求、断点续传、解压缩

ps&#xff1a;本文转载自网络&#xff1a;http://ryan.easymorse.com/?p12 感谢作者 工程完整代码下载地址&#xff1a;RequestTestDownload1 可完成&#xff1a; 下载指定链接的zip压缩文件存放在Documents目录下支持断点续传显示下载进度解压到指定目录—————————…

熊仔科技Steamduino PIC18F46J50主控板 部分原理图

目录连接情况原理图实物图Proteus 8 Frofessional v8.9 SP2 从学长那嫖的&#xff0c;本来貌似是要用来做写字机器人的&#xff0c;后面学长换成Arduino UNO了。 网上找不到资料&#xff0c;用万用表简单测了测没有丝印部分的连接情况 连接情况 RD2/PMD2/RP19 USR_LED…

【51单片机快速入门指南】3.3:USART 串口通信

目录快速使用硬知识串行口相关寄存器串行口控制寄存器SCON和PCON串行口数据缓冲寄存器SBUF从机地址控制寄存器SADEN和SADDR与串行口中断相关的寄存器IE和IPH、IP串行口工作模式串行口工作模式0&#xff1a;同步移位寄存器串行口工作模式1&#xff1a;8位UART&#xff0c;波特率…

Spring JTA应用JOTM Atomikos II JOTM

上节建立了一个简单的Java Application以及所需要的数据库和数据表&#xff0c;本节将介绍JOTM在Spring中的配置。 JOTM(Java Open Transaction Manager)是ObjectWeb的一个开源JTA实现&#xff0c;本身也是开源应用程序服务器JOnAS(Java Open Application Server)的一部分&…

从包中构建瓦片服务器

SWITCH2OSM 切换到OPENSTREETMAP 丰富的数据 OpenStreetMap数据丰富而详细&#xff0c;包含与实地人相关的大量数据 - 收集的数据。 功能包括&#xff1a; 道路&#xff0c;铁路&#xff0c;水路等餐厅&#xff0c;商店&#xff0c;车站&#xff0c;自动取款机等。步行和自行车…

【51单片机快速入门指南】4: 软件 I2C

目录硬知识I2C 介绍I2C 物理层I2C 协议层数据有效性规定起始和停止信号应答响应总线的寻址方式数据传输示例程序Soft_I2C.cSoft_I2C.h普中51-单核-A2 STC89C52 Keil uVision V5.29.0.0 PK51 Prof.Developers Kit Version:9.60.0.0 硬知识 摘自《普中 51 单片机开发攻略》 I2…

【51单片机快速入门指南】4.1: I2C 与 AT24C02 (EEPROM) 的跨页连续读写

目录硬知识AT24Cxx 介绍引脚排列引脚说明存储结构器件寻址器件操作待机模式存储复位写操作字节写页写应答查询读操作当前地址读随机读顺序读示例程序24C02.c24C02.h测试程序main.c实验现象通讯波形写入部分读取部分普中51-单核-A2 STC89C52 Keil uVision V5.29.0.0 PK51 Prof…