在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,一经查实,立即删除!

相关文章

ASP.Net 获取服务器信息

1: Response.Write("服务器机器名&#xff1a;" Server.MachineName); 2: Response.Write("<br/>");3: Response.Write("服务器IP地址&#xff1a;" Request.ServerVariables["LOCAL_ADDR"]);4: Response.Write("<br/…

POJ 2456 - Aggressive cows(二分)

Description Farmer John has built a new long barn, with N (2 < N < 100,000) stalls. The stalls are located along a straight line at positions x1,…,xN (0 < xi < 1,000,000,000). His C (2 < C < N) cows don’t like this barn layout and becom…

〖Android〗存在多个Android设备时,使用Shell脚本选择一个Android设备

Shell脚本&#xff1a; #!/bin/bash devices( $(adb devices|grep device$|awk {print $1}|xargs echo) )case ${#devices[]} in0 )echo "cant found a android device!";;1 )serial$devices;;* )select serial in ${devices[]}; dobreak;done;; esacif [[ -z $seria…

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

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

分享20个Android游戏源代码。以后看看。

分享20个Android游戏源码&#xff0c;希望大家喜欢哈&#xff01;http://www.apkbus.com/android-21834-1-1.htmlAndroid 疯狂足球游戏源码http://www.apkbus.com/android-20986-1-1.htmlandroid源码捏苍蝇游戏源码http://www.apkbus.com/android-20987-1-1.htmlAndroid游戏源码…

【.Net】C# 将Access中时间段条件查询的数据添加到ListView中

一、让ListView控件显示表头的方法 在窗体中添加ListView 空间&#xff0c;其属性中设置&#xff1a;View属性设置为&#xff1a;Detail&#xff0c;Columns集合中添加表头中的文字。 二、利用代码给ListView添加Item。 首先&#xff0c;ListView的Item属性包括Items和SubItems…

获取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位自动…

EBS并发管理器请求汇总(按照并发消耗时间,等待时间,平均等待事件等汇总)...

此数据集用于确定正在使用中并发管理器&#xff0c;并可与实际的在启动时分配的并发管理器。而且考虑完成状态为 正常/警告 的请求。 select q.concurrent_queue_name,count(*) cnt,sum(r.actual_completion_date - r.actual_start_date) * 24 elapsed,avg(r.actual_completion…

Linux内核RCU(Read Copy Update)锁简析

在非常早曾经&#xff0c;大概是2009年的时候。写过一篇关于Linux RCU锁的文章《RCU锁在linux内核的演变》&#xff0c;如今我承认。那个时候我尽管懂了RCU锁&#xff0c;可是我没有能力用一种非常easy的描写叙述把Linux的实现给展示出来&#xff0c;有道是你能给别人用你自己的…

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…

Oracle常用查看表结构命令

转载自:http://blog.520591.com/1301 获取表&#xff1a; select table_name from user_tables; //当前用户的表 select table_name from all_tables; //所有用户的表 select table_name from dba_tables; //包括系统表 select table_name from dba_tables where owner’用户名…

Centos7 安装oracle数据库

参考的内容&#xff1a; http://docs.oracle.com/cd/E11882_01/install.112/e24325/toc.htm#CHDCBCJF http://www.cnblogs.com/yingsong/p/6031452.html http://www.cnblogs.com/yingsong/p/6031235.html 步骤主要是有&#xff1a; 1、安装依赖的 软件包 2、创建用户和目录&…

ABAP常见面试问题

ABAP常见面试问题 1. What is the typical structure of an ABAP program? 2. What are field symbols and field groups.? Have you used "component idx of structure" clause with field groups? 3. What should be the approach for writing a BDC program? …

车牌识别之车牌定位(方案总结)

尊敬原作者&#xff0c;转自:http://blog.csdn.net/hqw7286/article/details/5810353 一直研究车牌识别算法&#xff0c;主要关注车牌定位和字符识别。我想分享一下我对车牌定位的看法。 从根本上讲&#xff0c;车牌定位的算法分为三类&#xff0c;一类是基于边缘的&#xff0c…

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设置主频 …

CentOS多网卡重命名配置

CentOS多网卡重命名配置在CentOS7中我安装了3块网卡&#xff0c;但是名字是enoxxxxx的格式&#xff0c;让我这个有强迫症的***座很是不爽&#xff0c;以下是我配置网卡并且重命名为ethx的详细步骤前提工作要做好&#xff1a;1.查看网卡UUID# nmcli con show名称 UUID …

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…