数组和指针都是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 的捷径。