前述
大家在平常的编程过程应该会碰到各种奇葩的问题吧,反正我最近是碰到了一次,再此跟大家分享一下。事情的原因是我在程序中增加了一个变量,然后就会导致程序每次都会进入异常。
示例代码
我将代码简化了,使用两个模块来演示这个问题。第一个模块是dev模块。
下面是dev模块的头文件,dev结构体中有一个数组。
#include #include typedef struct { int a_[100]; char b_;}dev;void devinit(dev *c_this);
下面是dev模块的源文件代码,里面只有一个memset。
#include dev.hvoid devinit(dev *c_this) { memset(c_this, 0, sizeof(dev));}
第二个模块是devmanager,相关数据结构如下:
#include dev.h#pragma pack(1)typedef struct { dev uart_; char num_; // adding this variable causes a crash dev iic_;}devmanage;#pragma pack()devmanage dev_manage;void devmanagerinit(void) { devmanage *c_this = &dev_manage; memset(c_this, 0, sizeof(devmanage)); devinit(&c_this->uart_); devinit(&c_this->iic_);}
devmanage结构体包含uart以及iic设备,以及我新加入的一个num_变量,由于新增了num_变量以及与之相关的业务会导致每次调试目标板都会进入异常。
尝试解决异常问题
根据调试情况看,每次都会出现异常,说明是个小问题。就怕偶尔出现异常,不容易复现。
思路应该非常清晰,出现异常时候查看lr寄存器的值,lr寄存器主要有两个功能。
保存子程序返回地址。使用bl或blx时,跳转指令自动把返回地址放入r14中
当异常发生时,异常模式的r14用来保存异常返回地址
根据lr寄存器的值,从而定位到是执行devinit(&c_this->iic_)函数中的memset导致的。
第一反应是devinit中传入的对象可能为空,操作了非法内存才导致的错误。于是又重新调试了一遍,发现devinit中对象的地址并不为空,而且就是等于实体对象中设备的地址。
这一刻我陷入了深深的自我怀疑,memset难道不是这样用的?难道不是传入一个地址,清0,然后sizeof(devmanage)就完事了?
我这代码怎么会出错,memset就是这样用的,天王老子来我也是对的,这样的心理是不是也深度还原了碰到问题时的你们。
如果是你,该如何继续...
救命稻草
有人说,汇编是最后的救命稻草。那我也尝试抓住这根稻草,出现问题时如下图:
问题主要出现红色箭头指向的这一行,经过查询资料得知_aeabi_memclr4是一个用于arm嵌入式系统的函数,用于将内存区域清零。函数名中的_a表示该函数符合arm嵌入式应用二进制接口(embedded application binary interface,eabi)规范。在调用_aeabi_memclr4时,需要确保传入的内存地址是四字节对齐的。再看图片中的对象地址为0x200001bd,刚好比四字节对齐地址0x200001bc多了一个字节。
再回头看devmanage对象,这里使用了伪指令#pragma pack(1)让内存分配进行单字节对齐。因为dev对象是按照四字节对齐的,紧接着引入了新的成员num_占用一个字节。所以导致iic_对象的地址相对于未增加变量之前的地址偏移了一个字节,导致不是四字节对齐的了,从而引发了错误。
就示例中的代码而言,只需要把强制devmanage对象单字节对齐的功能删除即可解决问题,因为默认情况下是四字节对齐的。
成员分配
由于#pragma pack(1)具有作用域限制,这里其实只是对num_变量做了单字节对齐的限制,而并没有对dev对象的内存分配起到限制作用,dev对象仍然是按照4字节对齐的(默认值),所以dev对象的所占用的长度一定是101*4=404字节。而整个devmanager对象的大小就101 * 4 + 1 + 101 * 4 = 809字节,成员分配如下图所示。
最后
到最后在抛出一个问题,对于上述的代码在vscode中使用gcc编译执行为何没有问题?
iQOO Neo7 SE, 性能神机,比快更强
N-Sat110号卫星参数表
特斯拉的车载地图供应商四维图新与方正电机合作
!!特价 R2600C R2600C R2600B R260
多媒体硬盘播放器图像分辨率/视频信噪比
程序中增加一个变量导致异常的分析
工厂操作系统装机量终破8亿
为什么Allegro调入网络表后看不到元器件
功率表的使用_功率表的使用注意事项
新年第一机 红米Note4X情人节发布!999元起售
夏季如何让您的光伏电站发电量更胜一筹?
智能家居DIY市场将呈现快速增长趋势,预计到2023年年复合增长率达35%
洗衣机保养方法
中国电信正在推动eSIM技术在智能终端以及物联网领域上的应用
如何利用TDE工业网关进行数控机床数据采集?
【展会邀约】6月1日与您相约成都IME
555循环定时器电路图
人工智能未来有这三种设计模式
未来无线充电技术的发展前景如何
消防巡检柜是什么,它是如何工作的