4本文引用地址:http://www.eepw.com.cn/article/201811/395096.htm

笔者曾经在《天灵灵地灵灵,遥控为何会失灵》一文中讲述过报文解析程序的一部分原理和设计,

“射频位到数据位采用了曼彻斯特编码形式,以射频位01表示数字位1,以射频位10表示数字位0,BCM采用上升沿触发中断的方式,根据相邻两个上升沿之间的时间间隔来赋值射频位。BCM根据遥控报文的格式提取出“数据场”中的射频位位流,然后进行曼彻斯特解码,计算出数据位位流,进而提取出字节形式的数据。。。。相邻两个上升沿的间隔取值只可能为2T、3T、4T,T为射频位位宽。”

前文所述“把数据位0或1这个‘大象’放到前面打开的‘冰箱’里”,指的就是赋值射频位的过程,

如果是2T,执行StoreRfBit(0); StoreRfBit(1);

如果是3T,执行StoreRfBit(1); StoreRfBit(0); StoreRfBit(1); 或者StoreRfBit(0); StoreRfBit(1);

StoreRfBit(0);

如果是4T,执行StoreRfBit(1); StoreRfBit(0); StoreRfBit(0); StoreRfBit(1);

遥控报文的格式非常规整,能够很容易地找到数据场的第一位,然后次序收完数据场的最后一位,本不该出现接收报文失败的问题。既然无法通过分析代码找出bug,那就只好祭出“调试大法”了。继续添加测试语句:

if(Rf_bit_count >= RF_RAWBIT_LEN){

Rf_frame_times++;

StoreRfCompleteIdx();

SetRfFrameComplete();

}

洒家这次没有吝惜RAM,开了个足以存储好几条报文射频位位宽数据的轮转型大数组,在判断接收到一帧完整的报文语句那里存储完整报文最后一个数据位在轮转型大数组中的下标位置,设置断点,运行到断点位置后,便可以从这个数组下标往前搜索,看看之前几个报文的射频位位宽数据是否有什么异常。

聪明的读者肯定已经抢先一步意识到了,射频位位宽没有出现任何异常。报文头很规则,数据场很整齐,报文尾部也很利落。

总之,所有的射频位数据安安静静地等待在时间的无涯荒野里,没有早一步,也没有晚一步,我遇上了,却没有轻轻地说一句:哦,原来你也在这里呵!

洒家揉了揉因为看数据看得有些发胀的眼睛,缓缓走到窗台前,眯缝起眼睛,打量了一下远处笼罩在一片蜃气之中的青山。每每遇到难解的问题,洒家总要到窗台阳光的沐浴中,眺望一下远处的山,让脑袋放空,然后闭上眼睛,静下心来,等着灵感不请自来。

中央空调沙沙的换气声音、同事的窃窃私语声不时入耳,我在心里不断回忆着有限的职业生涯里遇到的一个个bug,同时盘算着当前程序可能的缺陷。报文格式的分段解析肯定没问题,射频位位宽的判断也没问题,射频位的赋值也没问题,想来都没有问题,这个bug隐藏地够深的。

5

还得调试!行文至此,我不禁怀疑起了自己的智商,为什么基本上所有难题的解决都是靠调试解决的?没有一次是灵光电闪,看代码直捣黄龙,找出bug的?

被这个难题耗损了大半豪气的我,老老实实地开始调试起来。为了定位bug,笔者特意修改了程序结构,之前的程序是边接收边解析,实时性固然好,但是接收了几个数据便解析,不利于调试,所以改为收到两三条报文后,再集中进行解析。

洒家看代码找bug的本事没有,设计调试方案找bug的能力还是有的。修改程序之后,调试下来,问题就慢慢浮上水面了。程序解析出报文头部,进入数据场之后,在赋值射频位的过程中,结果出现了Rf_bit_count从288累加到33的情形!!!

奇怪了,288累加一下应该是289,怎么就变成33了?莫非1+1不等于2了不成?!!此处肯定有蹊跷,洒家打眼一瞧,马上看出了一点端倪,289和33正好差了一个256,就好像是把向高字节的进位吃掉了一般。问题开始变得有趣了!

Rf_bit_count是个16位的数据,好像突然变成了8位数据,显然,它的高位字节发生了不为人知的变化!写过多年代码的洒家,立马想到了是和Rf_bit_count临近空间的数据搞的鬼,回到定义位置一看,

uint8_t  Rx_buffer[RF_DATA_LEN];

uint8_t  Rx_rawbit[RF_RAWDATA_LEN];

uint16_t Rf_bit_count;

一切都了然了,Rf_bit_count挨着Rx_rawbit这个数组,肯定是Rx_rawbit这个数组搞的事,这个数组越了界,就会改变Rf_bit_count的数据,下面就简单了,看看是不是这回事!

我飞速地在纸上算了一下,当Rf_bit_count=288时,右移三位为36,Rf_rawbyte_idx =36时,Rx_rawbit数组正好越界,由于Rf_bit_count的位置正好在Rx_rawbit之后,而且所使用的处理器是大端模式,大端方式将高位存放在低地址,小端方式将低位存放在低地址。

所以,Rf_bit_count的高字节正好挨着Rx_rawbit数组,肯定是对Rx_rawbit[36]赋值为0了,导致Rf_bit_count本来是1的高字节变成了0,于是289就变成了33,这样一来,判断接收到一条完整报文的语句里的if语句里的条件肯定是false了,于是,好好的一帧数据就这样被漏掉了。

if(Rf_bit_count >= RF_RAWBIT_LEN){

SetRfFrameComplete();

}

当然,细心的读者可能会比较奇怪,只需要接收288个射频位,为什么Rf_bit_count还会累加到289呢?

这是因为,最后一个数据位可能是1也可能是0,最后统计到的射频位宽可能是2T、3T或者4T,它们执行的Rf_bit_count是不一样的,有时正好统计到288,那自然万事大吉,可以接收到报文,可是有时就会超过288,如前所述,这时它的高字节就有可能会被Rx_rawbit[36]吃掉,这就接收不到报文了。

后记

笔者学过一段时间的Java,Java对数组进行了一定的安全处理,在运行期间会自动判断数组下标是否越界,当时看的洒家羡慕得不得了。在计算机的世界里,C语言饱经沧桑,年头太老了,这种优异的特性显然指望不上,我等嵌入式工程师只能擦亮慧眼,始终保持警惕,要知道:数组越界真可怕,莫名就闯到了别人家。

c语言放空一个数组,数组越界真可怕,莫名就闯到了别人家相关推荐

  1. c语言如何输出整形数组,C语言 有一个整形数组a,有10个元素,要求输出数组中的全部元素...

    有一个整形数组a,有10个元素,要求输出数组中的全部元素 解题思路:引用数组中各元素的值有3种方法:1.下标法,如a[3];2.通过数组名计算数组元素的地址,找出元素的值 3.用指针变量指向数组元素. ...

  2. c语言放空一个数组,C++语言

    8种机械键盘轴体对比 本人程序员,要买一个写代码的键盘,请问红轴和茶轴怎么选? 本章学习字符相关内容. 让我们从介绍字符型(char)开始. 字符型,顾名思义,就是可以保存字符的数据类型,与int.d ...

  3. c语言如何用双重循环去重,c语言中一个一维数组怎样去重?

    满意答案 bigchou32 2013.04.13 采纳率:40%    等级:12 已帮助:6304人 #include #include using namespace std ; bool is ...

  4. 【C语言】数组的基本知识详细讲解(一维数组、二维数组、越界、存储.....

    接着上次的操作符的详解,让我们来简单了解C语言里的数组. 目录 一维数组的创建和初始化 一维数组的使用 一维数组的存储 二维数组的创建与初始化 二维数组的存储 数组的越界 总结 一维数组的创建和初始化 ...

  5. c语言编译系统是否检查越界,C语言编译器不检查数组下标越界

    这两天被人问了一个问题说假如C/C++访问下表越界的数组元素会报错么,于是充满好奇心的我动手试了一下,WTF,果然没有报错,但是会给程序带来莫名其妙的结果(比如十次的循环但是变成了死循环,但八次却可以 ...

  6. 2-结构体的最后一个成员的定义-C语言中的柔性数组-

    深入浅出C语言中的柔性数组 在日常的编程中,有时候需要在结构体中存放一个长度动态的字符串,一般的做法,是在结构体中定义一个指针成员,这个指针成员指向该字符串所在的动态内存空间,例如: [cpp] vi ...

  7. C语言中不检查数组下标是否越界。

    C语言中不检查数组下标是否越界. 转载于:https://www.cnblogs.com/AsmLearner/p/3405567.html

  8. c语言有一个已经排好的数组,C语言有一个已经排好序的数组。现输入一个数,要求按原来的规律将它插入数组中...

    C语言有一个已经排好序的数组.现输入一个数,要求按原来的规律将它插 入数组中 main() {inta[11]={1,4,6,9,13,16,19,28,40,100};inttemp1,temp2, ...

  9. 【C语言笔记】关于数组的一个陷阱!

    问题 两个数组元素的地址相减得到什么? 我们先看一段代码: #include <stdio.h> int main(void) {int a[]={0,1,2,3,4,5};printf( ...

最新文章

  1. 基于【CentOS-7+ Ambari 2.7.0 + HDP 3.0】搭建HAWQ数据仓库01 —— 准备环境,搭建本地仓库,安装ambari...
  2. [Flex] 组件Tree系列 —— 阻止用户点击选中Tree中分支节点
  3. HighNewTech:带你解读云计算、雾计算(Fog Computing)、边缘计算(Edge Computing)的前世今生
  4. 【vulnhub】靶机- [DC系列]DC9(附靶机))
  5. linux更改桌面壁纸的脚本,自动更换桌面壁纸的脚本,支持Ubuntu 18.04系统
  6. ARM MOV和 LDR指令关系
  7. MySql-phpMyAdmin
  8. Google菜市场(Android Market)上不去的解决方法
  9. 陆奇、雷军、熊晓鸽聊疫情后的创业风口
  10. Oracle 进程 说明
  11. JS 字符串去除首尾空格
  12. 配置无线AP 采用POE供电模块怎么配置无线AP没有POE交换机
  13. android 360全景视频,360度全景视频之VR播放
  14. 会议OA项目之我的审批签字功能
  15. cubieboard服务器系统,Cubieboard 1搭建服务器安装Linux/Debian系统并移至内置Nand中
  16. HTML基础知识概要面试必备
  17. 新疆大盘鸡的标准做法
  18. idea 一次启动多服务配置
  19. 复古传奇服务器维护时间,复古传奇手游刷怪时间
  20. Cross-X Learning for Fine-Grained Visual Categorization

热门文章

  1. 全角和半角的区别及使用方式
  2. 交互式设计--如何让你的界面简约
  3. 最标准的html模板
  4. oceanbase安装记录
  5. 【GANs学习笔记】(十六)CGAN、TRIPLEGAN
  6. 个人电脑秒变服务器 简单几步,你的电脑也可以成为服务器 (内网穿透)
  7. Gvim中实现特定行文本的替换
  8. Apple Music(应用内打开Apple Music)
  9. HTML怎么在背景中加视频,视频加背景图片 怎样把一个视频嵌入到一个图片中
  10. OPPO手机进水不读卡,修复