數組和指針都是C里面的好東西,但是一旦使用不當,真的會讓人抓狂。
下面是寫程序時遇到的一次數組越界的經歷,感覺對以后寫程序有點啟發,所以記錄下來。
起因:
我想用OLED動態顯示一組浮點數,而且浮點數的長度是不定的。
1、如果只是單純的顯示,沒有消隱的話,上一次的長數的據殘留會影響下一次短長度數據的顯示。
2、如果顯示一次就清空一次顯示區域的話,數據會一直抖動,一開始以為是我刷新頻率不夠,故把刷新頻率由100HZ改為1000HZ,但是效果還是和之前一樣!
后來想想也是,不管我把刷新頻率改為多少,清空后的空白和顯示的數據都是相同的頻率。1000hz顯示數據,那么也是1000hz的空白。所以會抖動嚴重。
3、把數據的每一位都取出來單獨顯示,但是這樣就又帶來了數據對齊的問題。不爽,不好看,棄之。
4、使用sprintf格式化需要顯示的數據為字符串。然后用OLED的顯示字符串的方式顯示。
於是有了下面這樣的程序:
sprintf((char *)weight_string,"%.1f",weight); //格式化為字符串 Clear_Left_Num(money_string); //消除殘余 OLED_Show_String(42,2,weight_string); sprintf((char *)price_string,"%d",price); Clear_Left_Num(money_string); OLED_Show_String(42,4,price_string);
這段程序在定時器中斷函數中調用。weight 和 price 就是我想顯示的浮點數。
先格式化為字符串,然后顯示。OLED_Show_String() 的前兩個參數是字符的起始顯示坐標。
Clear_Left_Num 函數如下:
void Clear_Left_Num(unsigned char *num_string) { while(*num_string != '.') num_string++; //一位小數點后面的數據用空格刷新 *(num_string+2) = ' '; *(num_string+3) = ' '; *(num_string+4) = ' '; }
思路就是把小數點后一位后面的殘余數據用空格刷新。
但是實驗現象是在顯示完第一行數據之后,本來應該在第二行顯示第二個數據,但是他 在第一行數據的后面又顯示了第二行的數據!!也就是說第二行數據顯示了兩次。
為什么會顯示兩次呢?我程序中就寫了一次啊、、、
分析:
既然是顯示的問題,那就先看看這個顯示函數!
/*---------------------------------- **函數名稱:OLED_Show_String **功能描述:光標處顯示字符串,字符串可以用數組表示,unsigned char string_2[] = {"THIS IS A TEST "}; **參數說明:X,Y為坐標 * chr:字符串首地址 **作者:Andrew **日期:2018.1.24 -----------------------------------*/ void OLED_Show_String(u8 x, u8 y, u8 *chr) { u8 j=0; while (chr[j]!='\0') { OLED_ShowChar(x,y,chr[j]); x+= 8 ; if(x>120){x=0;y+=2;} //自動換行寫 j++; } }
原來這個函數會在數組結束之前,顯示數組的全部內容。因為數組的最后一個結尾標志是 '\0’
那么,上面第一行一直在顯示,說明他可能沒有遇到數組結束標識符。
查看數組定義的大小:
unsigned char weight_string[7] = {0}; unsigned char price_string[3] = {0};
原來 weight_string 數組的最后一個結束標志被我賦值成了空格。那么他就會一直讀取存儲在這個數組后面的內存數據,並且給顯示出來。也就是所謂的“數組越界”。
幸好我們只是讀取顯示,並沒有改寫這個數據!
既然他顯示的是第二行的數據,說明第二行的數據就是存儲在在這個數組后面的內存中。
查看編譯器生成的map文件:
果然,第二個數組緊鄰着第一個數組存儲。
第一個數組讀取越界之后,讀到了第二個數組。
到此,問題解決。
總結:
一定要看到程序的內在聯系。分析內存雖然困難,但是卻是找到煩人bug 的捷徑。