資料斷點調試

來源:互聯網
上載者:User
全域變數的使用與資料斷點調試 (2007-05-19 15:42:03)

 
分類:學習、工作
    先前參與的項目,雖然使用的是面向過程的C語言,但軟體架構設計,模組的封裝,資料處理獨立於菜單實現等等,使得軟體具有物件導向的優點。物件導向可以使我們盡量的避免使用全域變數。因為學校的老師,公司的前輩都在強調全域變數在面向過程中的隱患。而如今我接手的項目,裡面卻使用了很多全域變數,軟體完全是面向過程的設計方法。突如其來的這麼多全域變數,讓人感覺不適應,而恰恰讓我遇上了一次全域變數帶來的問題。記錄和總結一下問題的解決過程。
       在處理一個問題的時候,發現一個全域變數(char TempStatus;)的值偶爾出現不是我所期望的,於是向前追溯,找到最後一個指派陳述式 TempStatus = 1;在這一過程中沒有再修改過,在賦值後加列印,竟然出現前後連續兩個printf輸出的該變數的值不一樣的情況,更別說後面再用了。出現這樣的情況,有兩種可能,一是該全域變數在別的進程中被修改了;二是與該全域變數地址接近的某個全域變數出現越界了。要是第一種情況還比較好處理,如果是第二中情況,那就非常棘手了。
      先排除第一種情況,要確認是否是在別的進程被修改了,就需要確認在什麼地方修改的。搜尋該全域變數,好傢夥,出來一大螢幕,汗!!看一邊都得半天,要在每個地方加一個printf逐一排除,那得多長時間啊,鬱悶!思路受阻。此時不禁懷戀set與get的好,你說要是寫上以下這樣的兩個函數給別的地方調用該多好啊!
void SetTempStatus(char val)

      TempStatus = val;

 
void GetTempStatus(voild)

     return TempStatus;

同時將TempStatus 定義為static char TempStatus;在要用的時候調用get函數,在要賦值的地方調用set函數。這樣一來,我要確定是否有別的地方修改就只要在set函數裡面加個printf就可以了,確定是被別的地方修改後只要在set函數裡面加個斷點,一單步就立刻定位修改的地方了。多簡單!可實際項目中並沒有這樣設計,沒辦法了。

         那就排除溢出情況吧。一般溢出都是該全域變數前的變數越界操作引起。於是找到該變數定義的地方:
int iDataSize;
char TempStatus;
unsigned int DataLen;
struct_table   DataTable[40]={0};
stuct_table1  DataTable1[20];
前面是一個整形資料,出現越界操作的不太可能,莫非是後面的數組?一搜尋,又是一大屏,而且難度更大,你不知道它什麼時候在什麼地方出現越界溢出,汗,汗!!!
       就這樣整整鬱悶了大半天,沒法確定問題的根源,也就沒法徹底解決問題,毫無進展。腦門子冒汗了,休息休息再說,還別說有的時候無路可走了,放鬆一下回來真有豁然開朗的感覺。全域變數的值不是我期望的,而我自己沒有做賦值,那肯定就是被修改了,不管是什麼原因,最終就是要確定它在什麼地方被修改的。於是想到了,項目方案的一個視覺化檢視裡面有一個資料斷點(DataBreakpoint)的功能,也許使用這一功能也許就能找到意外修改的地方。
      視覺化檢視在工作中很少有同事用,資料斷點就更少有人用。這也是我第一次用,憑藉一點理解試一下。理解中資料斷點,應該是對某一變數的地址所對應的空間進行監控,一旦該地址對應的空間被修改,就會立即產生中斷,並將程式的運行指標指向修改的語句。由此肯定能確定問題所在。
      因為全域變數在很多地方都有賦值,如果一開始就給該變數設資料斷點(DataBreakpoint),肯定會產生很多中斷。而我只需要關注該變數在我賦值以後的變化情況,由此在我的指派陳述式TempStatus = 1;處先設一個代碼斷點(Code Breakpoint)讓程式先運行到這裡,然後設定該變數的資料斷點,讓程式繼續運行,這個時候一個很奇怪的中斷出現了,程式的運行指標指在語句 DataTable1[iIndex].value = 0;程式在一個毫不相干的指派陳述式中斷了,那肯定是數組DataTable1越界修改了TempData對應的空間,喜!再看該數組的下標,竟然是-1。問題找到了,再看看該語句所在的函數:
int GetDataIndex(char data)
{
    .....
    for(i=0;i<DataLen;i++)
     {
        if(DataTable[i].value == data)
           return i/2;
     }
   return -1;
}
void  CreateTable(void)

     int iIndex;
       ........
     iIndex = GetDataIndex(data);
      .........
     DataTable1[iIndex].value = 0;

一看問題出來了,函數CreateTable()沒有對變數iIndex作出錯處理,在iIndex出錯等於-1時導致後續的資料越界操作。向前越界結構體stuct_table1的大小,剛好使得value指向了TempStatus。問題解決就很容易了,只要將函數改成:
void  CreateTable(char data)

     int iIndex;
       ........
     iIndex = GetDataIndex(data);
     if(-1 == iIndex)
         return;
      .........
     DataTable1[iIndex].value = 0;

往往是這樣,找問題的根源要用九牛二虎之力,而解決問題只需吹灰之力;
      解決了問題,回過頭來,看看變數的定義。TempStatus 和DataTable1之間還隔了一個很大的數組,他兩怎麼會“勾搭”上的?做個實驗,列印這些變數的地址,發現數組DataTable的地址與其他幾個有很大區別,有同事說,是因為它被初始化賦初值了,跟其他幾個全域變數不在一個地區。這樣也就好理解為什麼DataTable1能夠跨過一個大數組跟TempData扯上關係。
      問題解決了,總結一下。一:還是要強調盡量避免使用全域變數,如果非用不可,畢竟還是面向過程設計,應該想到使用set和get,並用static定義加以限制。這樣可以減少不少的麻煩,也便於以後尋找問題;二,就是要確保函數的健壯性,要有意識的去考慮出錯的情況,並做出正確的處理。這樣的處理是非常有必要的,往往一些隱患就在於沒有對出錯情況做出正確的處理;三,就是資料斷點的使用,這是個好東西,第一次使用就深深體會了它的好。
(註:這裡用到的代碼非實際代碼,只是為說明問題,出之隨意)

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.