前几天在写C51程序时用到了递归,简略程序如下:
voidWRITE_ADD(ucharaddr,ucharwbyte){START();//先发送起始信号WRITE_BYTE(0xa0);//设备地址+W指令if(!ERROR_Flag)//正确收到应对{WRITE_BYTE(addr);//写入地址}else{ERROR_Flag=0;//清过错标志WRITE_ADD(addr,wbyte);//从头写入}if(!ERROR_Flag)//地址收到正确应对{WRITE_BYTE(wbyte);//发送要写入的数据}else{ERROR_Flag=0;//清过错标志WRITE_ADD(addr,wbyte);//从头写入}if(!ERROR_Flag)//正确收到应对{STOP();//中止}else{ERROR_Flag=0;//清过错标志WRITE_ADD(addr,wbyte);//从头写入}}
编译时呈现如下正告:
warning C265: ‘_WRITE_ADD’: recursive call to non-reentrant funcTIon(循环调用了非可重入函数)。
经过查找材料之后,解决方法是在函数后参加关键字,使函数变成可重入函数:
回来值 函数名(形参) reentrant
上面的函数是有过错的,可重入函数不能传递bit类型的变量。在多任务体系中,可重入函数也不要用大局变量,多个函数一起调用时或许会使变量呈现多个值,可是在单任务体系中,个人认为某些时分下是能够运用的。只需不呈现改动变量值的状况。
一、可重入函数
首要对重入函数进行一下阐明。
可重入函数首要运用在多任务环境中,一个可重入的函数简略来说便是能够被中止的函数。也便是说这个函数履行的任何时刻中止它,转入另一段代码,回来操控时不会呈现什么过错,而不行重入的函数因为运用了体系资源,比方大局向量、中止向量表等,假如函数被中止的话或许会产生过错。
在Keil手册中对可重入函数的解说为:一个可重入函数能够在同一时间被几个进程同享。当一个函数可重入运行时,其他进程可中止履行,并开端履行相同的可重入函数。正常状况,C51编译器重的函数不能重入。原因是函数的参数和局部变量保存在固定的存储区中。经过reentrant函数特点答应声明函数可重入,因而可重复调用。可重入函数能够被递归调用,可一起被两个或多个进程调用。可重入函数经常在实时运用或许在中止和非中止有必要共用一个函数的状况下被运用。假如函数界说为特点reentrant,那么这个可重入函数,一个可重入的仓库区一起在内部和外部存储区模仿,这是由存储形式来决议的。假如是SMALL形式,则在idata存储区模仿可重入仓库。假如是COMPACT形式,那么在pdata存储区模仿可重入函数仓库。假如是LARGE形式可重入函数在xdata存储区模仿可重入仓库。
二、可重入函数与函数的可重入
可重入函数与函数的可重入是两个不同的概念。
在C51中假如咱们界说以下函数:
int funcTIon(int a, int b, int c) compact reentrant
{
long x, y, z;
。..。
}
因为声明为reentrant特点,因而函数为可重入函数,其参数(a,b,c)和局部变量(x,y,z)存储在模仿仓库(simulated stack),因为是compact形式,因而在pdata区。
假如没有特意的声明compact,则会默许的为small,会在idata区模仿仓库。假如是large则会在xdata区。这是可重入函数。(单片机的“硬件栈”,其实只要一个,便是咱们一般说的SP,它是在内部RAM中的)
但有些函数未声明为reentrant,可是可重入的。大多是以汇编来编写的,其参数和局部变量存储在寄存器中(data),在C51的库函数中有许多这样的函数,它们是可重入的,但未用reentrant声明。
三、解说
一般的函数的形参和局部变量的存储是存在大局变量区(在《大局变量和局部变量存储》中详细的解说),在递归调用的时分上一层次的局部变量会被本层次调用冲掉。经过reentrant,编译器会构成模仿栈为形参和局部变量分配内存。假如函数递归或许嵌套的次数太多,也会产生栈溢出(关于该模仿栈的巨细能够在STARTUP.A51中修正)。
关于重入函数的模仿栈与单片机内的栈不同,模仿栈是由最顶端往下递减的,而sp则是grow up的。
在函数的递归或许经过函数指针调用函数时,假如被调用的函数中有字符串常量,有时会提示“WARNING13:RECURSIVECALLTOSEGMENT”
其详细的解决方法见转载文章“Keil“RECURSIVECALLTOSEGMENT”彻底解决”。