久久久久久久999_99精品久久精品一区二区爱城_成人欧美一区二区三区在线播放_国产精品日本一区二区不卡视频_国产午夜视频_欧美精品在线观看免费

專注電子技術學習與研究
當前位置:單片機教程網 >> 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.

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

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

  有一個以數組的地址指針為參數的函數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++的靈魂啊——你知道,靈魂這東西,雖然有可能是個天使,也有可能是個惡魔。

 

關閉窗口

相關文章

主站蜘蛛池模板: 欧美影院 | 久久久久国产一区二区三区四区 | 国产免费观看视频 | 亚洲欧美日韩电影 | 国产一区二区三区免费观看在线 | 激情av在线 | 成人福利视频 | 91免费看片| 黄色av大片 | 日日操夜夜干 | 午夜免费看 | 黄色片免费看视频 | av在线视 | 1000部精品久久久久久久久 | 蜜桃视频在线观看www社区 | 视频一区在线观看 | 国产成人91视频 | 毛片a级 | 国产精品揄拍一区二区 | 欧美最猛性xxxxx亚洲精品 | 日本精品在线播放 | 亚洲一区二区三区在线播放 | 毛片大全 | 99热在线观看精品 | 欧美成人a∨高清免费观看 91伊人 | 久久免费精品视频 | 久久69精品久久久久久国产越南 | 影音先锋欧美资源 | 日韩在线观看中文字幕 | 久久国产精品视频 | 日本久草 | 欧美日韩精品免费 | 精品影视 | 国产精品高潮呻吟久久av野狼 | 久久久久久久久久久久一区二区 | 欧美午夜精品 | 99精品久久| 日韩免费福利视频 | 91精品久久久久久综合五月天 | 91 在线| 一区二区三区免费看 |