首先,在C语言中,清空结构体的方法,我们一般会采用 memset函数。
其原型是:
void * memset ( void * ptr, int value, size_t num );
函数功能: 填充内存块
将ptr指向的内存块的前num个字节设置为指定值 value(解释为unsigned char).
函数参数:
- ptr: 指向要填充的内存块的指针。
- value:要设置的值。该值以int形式传递,但是该函数使用此值的无符号char转换填充内存块。
- num: 要设置为value的字节数。size_t是无符号整数类型。
在这里,我们采用的也是 memset 清空结构体。但是,由于 num 值设置的比实际的结构体字节数还要大,导致也清空了其他变量的值。
关于变量与内存的分配,我们知道:
- 静态区:保存自动全局变量和static变量(包括static全局和局部变量)。静态区的内容在总个程序的生命周期内都存在,由编译器在编译的时候分配。
- 栈:保存局部变量。栈上的内容只在函数的范围内存在,当函数运行结束,这些内容也会自动被销毁。其特点是效率高,但空间大小有限。
- 堆:由malloc系列函数或new操作符分配的内存。其生命周期由free或delete决定。
因此,我们定义的全局变量,全部在静态区,而且,地址是连续,所以,如果在使用 memset()清空定义全局变量的数组或者结构体时。一定要确保,num 的长度 恰好是 数据或结构体占空的字节数。否则会清空其他定义的变量。
具体可以看我出错的情况:
我定义一个几个结构体:
typedef enum
{
SLAVE_FRAME_IDLE = 0,//空闲状态
SLAVE_FRAME_HANDER,//帧头
SLAVE_FRAME_SLAVE_ID,//丛机ID
SLAVE_FRAME_DRIVER_ID,//驱动板ID
SLAVE_FRAME_OPERATE,//操作码
SLAVE_FRAME_COMMAND,//帧命令
SLAVE_FRAME_DATA_LENGTH,//数据量长度
SLAVE_FRAME_FRAME_LENGTH,//帧长度
SLAVE_FRAME_DATA,//帧数据
SLAVE_FRAME_CRC,//校验
SLAVE_FRAME_FINISH,//完成
}MasterUsartRxFrame_E;
typedef struct
{
uint8_t FrameHander;//帧头
uint8_t SlaveID;//丛机地址 ID
uint8_t DriverID;//驱动板ID
uint8_t Operating;//操作码
uint16_t Command;//命令
uint8_t DataLength;//数据长度
uint8_t FrameLength;//帧长度
uint16_t* pData;//数据 --> 只对写有效
uint16_t CheckSum;//CRC校验
}SlaveFrameData_T;
typedef struct
{
MasterUsartRxFrame_E FrameStatus;//帧状态
SlaveFrameData_T Package;//帧包
}SlaveUsartData_T;
extern SlaveUsartData_T tUART3_RxData;
然后,在我想清空 tUART3_RxData.Package 结构体的数据时,
本来应该是 采用:
memset(&tUART3_RxData.Package ,0,sizeof(SlaveFrameData_T));
但是,我在写的时候,写成了 SlaveUsartData_T:
memset(&tUART3_RxData.Package ,0,sizeof(SlaveUsartData_T));//bug 这里长度溢出,会导致清空其他变量
所以导致 内存静态区中,定义在 tUART3_RxData.Package 后面的变量给也给清空了。
我们可以查看 map 文件:
连接在 tUART3_RxData 后面的是 tEncryptManufacture 所以,或导致,tEncryptManufacture结构体中前面几个成员清零。
事实上,也确实如此。。
因此:在使用 memset 的时候,长度一定不能溢出。。