C51单片机中reentrant关键字的使用,关于MULTIPLE CALL TO FUNCTION警告的问题

📅 2026/7/16 21:15:15 👁️ 阅读次数 📝 编程学习
C51单片机中reentrant关键字的使用,关于MULTIPLE CALL TO FUNCTION警告的问题

关于可重入关键字reentrant的使用:

现象:

在一个项目中警告信息如下,提示该函数多次调用,因为该函数在串口中断和主循环中都有被调用。

影响:

如果在使用该函数期间被中断打断,而中断也调用了该函数,那么该函数的局部变量会被覆盖。当中断结束返回,则此时的变量已经被中断修改了,此时的数据不对,会出现意想不到的结果。
这就是:不可重入(non-reentrant)

原因:

Keil C51 为了省 RAM、提速,普通函数的参数和局部变量往往不放在硬件栈上,而是放在固定的静态内存里(类似全局变量)。
51单片机资源有限,为了提高运行效率,C51采用静态分配局部变量的方式,所以不可递归。而标准C语言的堆栈是动态分配的。

解决方案:

给需要重复调用的函数加上reentrant关键字,使用方式如下图(编译器会改成:参数/局部变量放到专用的可重入栈上,每次调用都有自己的一份,不再共用固定地址。)

这样中断里可以安全再调这个函数。
另外递归调用也可以(8051 上很少这么干,且很吃 RAM)
最后再编译发现警告消失。

注意:使用reentrant,要另外需要在启动文件中根据内存模型打开对应栈

3.IBPSTACK / XBPSTACK / PBPSTACK分别是什么?

它们对应三种内存模型下的可重入栈指针初始化

开关名字含义可重入栈放哪典型场景
IBPSTACKIdataBasePointer Stack内部 RAM(idata)Small 模型 +reentrant
PBPSTACKPdataBasePointer Stackpdata(一页 256 字节外存)Compact 模型 +reentrant
XBPSTACKXdataBasePointer Stackxdata(外部 RAM)Large 模型 +reentrant

建议:什么时候才需要reentrant

典型需求:

  1. 中断服务程序里要调用某个也会在主循环调用的函数,且该函数有局部变量/参数
  2. 需要递归
  3. 某些第三方库明确要求reentrant,并说明要开哪一种栈

更常见、更稳妥的做法:

  • 同个函数再复制一份(功能完全一样,但函数名不一样)
  • 中断里只置标志、收数据
  • 复杂处理放到主循环
  • 这样就不需要reentrant

参考文章:
1、C51的再入函数 reentrant RECURSIVE CALL TO SEGMENT
2、51单片机与递归的问题