久久久久久久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网 | 欧美综合国产精品久久丁香 | 在线a视频网站 | 999久久久 | 成人免费视频网站在线看 | 久久久久一区 | 日本精品视频一区二区三区四区 | 国产精品免费在线 | 色综合99 | 精国产品一区二区三区 | 精品国产一区二区三区日日嗨 | 中文字幕国产视频 | 亚洲91精品 | 午夜黄色| 国产做a爱片久久毛片 | 麻豆精品国产91久久久久久 | 成人精品国产 | 91免费高清 | 成人在线国产 | 成人午夜在线 | 欧美精品一区二区三区在线播放 | 精品久久久久久亚洲精品 | 91亚洲精品久久久电影 |