<acronym id="xonnx"></acronym>
      <td id="xonnx"></td>
    1. <pre id="xonnx"></pre>

      1. 專注電子技術學習與研究
        當前位置:單片機教程網 >> MCU設計實例 >> 瀏覽文章

        指針過界引起全局變量改變的錯誤

        作者:huqin   來源:本站原創   點擊數:  更新時間:2014年11月10日   【字體:

          最近,在寫程序的時候,碰到一個在自己看來非常不可思議的問題。當然,或者高手就覺得大驚少怪了,呵呵。

         

          以下是問題相關:

          平臺:MEGA64;

          編譯環境:codeVisonAVR;

          問題:一個全局變量在定義的時候直接賦值為0,但在程序運行過程中,沒有改變該變量的操作,可是奇怪的是,程序在運行一段時間之后,這個全局變量居然自動改變了。

          發現問題之后,我很難理解,一直無法找到原因。

          一開始,我就懷疑是不是哪里的存儲空間溢出重疊了,但由于對各種變量的存儲位置,不是十分的了解,也就沒有再對此做更深層的探究;其次,我懷疑是不是哪里的指針跳錯了,跳到全局變量那里,造成全局變量的改變,不過對于這一點,我又自認很仔細的檢查了程序,的確沒有發現指針跳錯的地方。

          現在看來,其實,我的兩個懷疑,都和指針有關的。

          后來,和志剛討論一下,他一開始就提出:是不是堆設置不夠。他說的堆是指hardware stack,我一開始不知道什么是hardware stack的。我猜他的意思是說,用于程序返回堆棧的空間太小了,以至于,硬件堆棧溢出,覆蓋了全局變量,自然造成全局變量的改變。

          一開始我沒有弄懂他的意思。明白之后,我覺得很有可能,不過,仔細看編譯結果,又大概可以排除這個可能了,以下是編譯結果:

        Bit variables area: 2h to 2h
        Bit variables size: 1 byte(s)

        Data Stack area: 100h to 4FFh
        Data Stack size: 1024 byte(s)
        Estimated Data Stack usage: 98 byte(s)

        Global variables area: 500h to 5DCh
        Global variables size: 221 byte(s)

        Hardware Stack area: 5DDh to 10FFh
        Hardware Stack size: 2851 byte(s)

          從最后一行可知,硬件堆棧大得很,4K的SRAM,已占2.8K。一般來說,就算是函數多層嵌入也不可能占如此之大的空間,網上說,就算浮點函數,40B都已經足夠。

          不過,由于志剛的提示,讓我注意到上面的編譯結果,以前我不知道全局變量是怎樣存儲的,現在就明白了,上面倒數3、4行,就是說全局變量的?梢,全局變量是保存在數據堆棧與硬件堆棧的中間,剛才志剛說,可能硬件堆棧溢出,造成全局變量的改變。雖然在這里這個可能不大了,但是,同理,會不會是數據堆棧溢出,造成全局變量的改變呢?因為,由上面幾行編譯結果可知,全局變量正處于數據堆棧和硬件堆棧之間,兩者的溢出皆有可能造成全局變量的改變。不過,“Estimated Data Stack usage: 98 byte(s)”,這里又很明顯提示,估算的數據堆棧實際用到的大小只有98B,也遠遠小于1024B。

          所以說,不管是數據堆棧還是硬件堆棧,兩者都很難簡單的溢出。

          下面是我在codeVisionAVR的幫助文檔上面找到關于RAM的結構圖:


         

          可知,codeVisionAVR是如何分配SRAM的(其它編譯器,各不相同的),這里簡單述說,以備以后參考。以maga64為例。(芯片資料說的4K SRAM,是不包括地址前面的100B的,也就是說SRAM的大小從數據堆棧開始數起)

          首先是,32個工作寄存器+64個I/O寄存器,這里占了RAM地址分配的前100字節,0H-99H;

          其次是,數據堆棧,而且是由高往低堆的,也就是,先從地址高處往低處進棧,由于它是用Y寄存器來做數據堆棧的指針的,所以,數據堆棧就從Y的初始值開始,到地址100H結尾,這個大小可以在編譯器工程設置里面設置,一般可以先編譯程序,看編譯估算的實際數據棧使用大小,再去定數據堆棧的大小,自然要定大一點,防止溢出。主要用于動態儲存局部變量、函數參數和中斷時各工作狀態寄存器的值;

          接著是,全局變量區域,這個是編譯器通過統計程序的全局變量數量而定的。用于保存全局變量;

          再接著是,硬件堆棧,以SP初始值開始,到全局變量最高地址為止,而且和數據堆棧一樣,也是由高往低堆的。用于保存函數返回地址;

          最后是,堆(heap),是malloc, calloc, realloc and free等鏈表函數用來建立鏈表的內存空間,如果不使用這個函數,必須設置為0.

          由上面可知,由于全局變量處于數據堆棧和硬件堆棧中間,而后兩者都是由高往低進棧的,所以說,如果正常溢出,只能是硬件堆棧溢出才可以造成全局變量的改變,而數據堆棧的溢出不可能造成這個問題?墒乾F在硬件堆棧這么多,也幾乎不可能是它溢出造成這個問題的。

          那會是什么原因造成上面那個問題的呢?分析之后,我重新再仔細查找程序,最后還是找到了原因。還好,這些程序原先不是我寫的,呵呵。

          有一個以數組的地址指針為參數的函數example(char *pTemp),主函數調用它的時候,傳一個4位數組temp[4]給它,如:example(temp);

          下面大略的代碼:

          main()

          {

            char temp[4];

            example(temp);

          }

          length[]={1,2,4};

          example(char *pTemp)

          {

            cLength=length[getIndex()];

            for(i=0;i<clength;i++)

            {

              *pTemp=readvalue();

              pTemp++;

            }

          }

          由于length是一個3位數組,如果getIndex()的值大于2,就造成cLength得到的值不在{1,2,4}內,而是儲存length[]={1,2,4}往后地址的值,這個值是不確定的,很有可能是遠遠大于4。這樣就造成,for里面的循環次數遠遠大于4次,因為,指針*pTemp本來指向一個4位的數組temp[4],現在由于pTemp++超過3次,已經不是指向temg[]這個4位數組了,而是大于temp[4]地址的地址了。

          我們知道,這個temp[]是一個局部變量,它應該保存在SRAM的數據堆棧里面,而數據堆棧是由高往低進棧的,緊挨著的就是全局變量區域,這個temp必定就是保存在離全局變量區域不遠的數據堆棧里面。于是,只要for的次數夠大,它不但改變了比temp后進的數據堆棧的數據,而且,跨過數據堆棧與全局變量區域的界限,直接修改了全局變量的某些值!

          其實,原來寫這個程序的人,已經做個防止getIndex()大于2的處理,可是呢,悲劇的是length[]卻少了一位數。呵呵,好在我究竟還是數了數它的位數(在實際的那個程序里,這個數組的位數自然不會是3位這么少,這樣一眼就可以看出來少不少。)。不過,如果不是志剛的提示,讓我提起心思去了解存儲空間的問題,即使我數出來位數少了一位,也不一定知道問題的根本原因所在!

          其實,這里就是指針惹的禍。不怪人家說,指針是C、C++的靈魂啊——你知道,靈魂這東西,雖然有可能是個天使,也有可能是個惡魔。

         

        關閉窗口

        相關文章

        欧美性色欧美精品视频,99热这里只有精品mp4,日韩高清亚洲日韩精品一区二区,2020国自产拍精品高潮