日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

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

發(fā)布時間:2023/12/9 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 在51单片机上使用递归的注意事项 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

目錄

  • 問題
  • 應對措施
  • 原理

普中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采用靜態(tài)分配局部變量的方式,所以一般情況下不可遞歸。
被中斷和非中斷函數(shù)調(diào)用的函數(shù),如果在非中斷狀態(tài)運行,發(fā)生中斷后,局部變量被破壞,中斷結束后再執(zhí)行就完全錯誤了,這個跟不能遞歸的原理是一樣的。
如對數(shù)組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;//遍歷數(shù)組,將小于錨定點的數(shù)swap到數(shù)組前部for (i = left + 1; i <= right; ++i) {if (v[i] < v[left]){ ++pos;temp = v[i];v[i] = v[pos];v[pos] = temp;}}//將錨定點的數(shù)與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");} }

得到的數(shù)據(jù)可能會出錯

應對措施

加上reentrant關鍵字后,輸出結果正確。

typedef int QuickSortType; void QuickSort(QuickSortType v[], int left, int right) reentrant {int i, pos;QuickSortType temp;if (left >= right)return;//取第一個元素為錨定點pos = left;//遍歷數(shù)組,將小于錨定點的數(shù)swap到數(shù)組前部for (i = left + 1; i <= right; ++i) {if (v[i] < v[left]){ ++pos;temp = v[i];v[i] = v[pos];v[pos] = temp;}}//將錨定點的數(shù)與swap至其正確位置pos;temp = v[left];v[left] = v[pos];v[pos] = temp;//遞歸該過程QuickSort(v, left, pos - 1);QuickSort(v, pos + 1, right); }


reentrant后也不能隨意使用遞歸:
由于51稀缺的資源,當數(shù)組長度達到46后也出現(xiàn)了錯誤,因此在51上盡量不要使用遞歸

原理

本節(jié)摘自Keil C51對C語言的關鍵詞擴展之十五: reentrant —— 昵稱90天可改
這篇博文寫的很詳細模擬堆棧,可重入函數(shù)調(diào)用,參數(shù)傳遞 —— hplog

???????reentrant聲明的函數(shù)為可重入函數(shù)。可重入的函數(shù)能夠被多個進程同時調(diào)用??芍厝牒瘮?shù)在執(zhí)行時,另外的進程可以中斷當前執(zhí)行的函數(shù),并且調(diào)用同一個函數(shù)。正常情況下,C51程序中的函數(shù)不能被遞歸地調(diào)用,這是由于函數(shù)的參數(shù)和局部變量都被保存在固定的地址,在遞歸調(diào)用時操作了相同存儲位置,導致數(shù)據(jù)被覆蓋。

???????使用reentrant聲明函數(shù)為可遞歸調(diào)用的可重入函數(shù):

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

???????可重入函數(shù),能夠被遞歸調(diào)用,也能被兩個以上的進程同時調(diào)用??芍厝牒瘮?shù)通常在實時應用或者中斷與非中斷程序共享相同函數(shù)這兩種情況下被使用。
每個可重入函數(shù)都有一個位于內(nèi)部ram或外部ram的模擬堆棧:

  • SMALL內(nèi)存模型下,可重入函數(shù)模擬堆棧位于idata區(qū);

  • COMPACT內(nèi)存模型下,可重入函數(shù)模擬堆棧位于pdata區(qū);

  • LARGE內(nèi)存模型下,可重入函數(shù)模擬堆棧位于xdata區(qū);

  • 使用reentrant聲明可重入函數(shù)須遵循的規(guī)則:

  • 可重入函數(shù)不支持位尋址變量,比如bit類型的參數(shù);

  • 可重入函數(shù)不能被alien函數(shù)調(diào)用;

  • 可重入函數(shù)不能被聲明為alien屬性(alien用于使能PL/M-51參數(shù)傳遞約定);

  • 可重入函數(shù)可以同時擁有其他屬性,比如using、interrupt、small、compact、large;

  • 返回地址被保存在硬件堆棧中;

  • 使用不同存儲模型的可重入函數(shù)能夠混合,但是各自函數(shù)聲明時必須指定存儲模型;

  • 三種內(nèi)存模型的可重入函數(shù)都有自己的對戰(zhàn)區(qū)和棧指針。比如在相同模塊中,定義了small和large類型的可重入函數(shù),則small和large類型的堆棧及其堆棧指針都被創(chuàng)建;

  • ???????可重入堆棧模擬體系,效率比較低下,但是由于8051自身缺乏適當尋址方法的硬件特性,所以推出這種堆棧模擬體系來滿足我們的可重入需求,在應用中 ,我們應該盡量不用或少用可重入函數(shù)。

    ???????可重入函數(shù)使用的模擬堆棧擁有獨立于8051硬件堆棧的棧指針。堆棧和堆棧指針在STARTUP.A51文件中被定義和初始化。

    ???????8051硬件堆棧向上增長,堆棧指針先加再壓棧,可重入模擬堆棧正好相反,其指針先減再壓棧。
    ???????STARTUP.A51啟動代碼聲明并初始化了模擬堆棧及其堆棧指針,如果使用可???????重入函數(shù)就必須修改啟動代碼指出哪個模擬堆棧須要初始化??梢栽趩哟a中修改模擬堆棧的起始地址。
    ???????可重入函數(shù)的參數(shù)傳遞,通過模擬堆棧的壓棧、出棧完成。
    ???????可重入函數(shù)的局部變量,也保存在模擬堆棧中,通過模擬堆棧指針訪問。

    總結

    以上是生活随笔為你收集整理的在51单片机上使用递归的注意事项的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。