类C类软件大多都支持通过指针对内存进行访问,使用不当很容易造成程序运行异常或异常重启的问题,记录下可能的原因和解决方案:
1、任务、线程堆栈溢出。
函数中使用大数组局部变量,超大结构体变量,容易导致堆栈溢出。
解决办法:
1.1、将大数组局部变量,超大结构体变量更改为指针变量,使用动态内存申请获取存储空间。
1.2、如果函数没有重入问题,可以简单的将大数组局部变量,超大结构体变量放到函数体外作为全局变量进行定义。
识别溢出的方法:
1.1、如果编译器有堆栈溢出检查功能,使能该功能。
1.2、自行实现堆栈溢出检查功能(在栈顶极限位置写入特定值,运行时检查此数值是否改变)
2、动态内存泄露,动态内存耗尽。
通过在动态内存分配释放函数里增加调试代码,统计分配和释放数据,观察是否存在内存耗尽问题。
3、访问未初始化的指针,导致非法地址访问或异常改写其他内存地址内容。
此类问题,可以通过代码审查发现,代码量很大时,比较难排查。
变量初始化属于编程习惯问题,可以养成是变量必初始化的习惯,避免此类问题。
4、数组写入越界。
在数组使用上,写入越界,很隐蔽,却很容易造成程序异常或重启。比如自定义如下函数:
无返回值 示例函数( 字符串类型 提示语 )
{
静态的 整数类型 计数=0;
字符类型 缓冲区[30];
sprintf( 缓冲区, "%(整数) :%(字符串)", ++计数, 提示语 );
。。。。
}
为节省对堆栈空间的耗用, 定义了够用就好的缓冲区, 在使用中,调用此函数,只要传入的提示语比较短,都没什么问题。
如果提示语又是其他函数构造的,其长度大部分时候不会过长,测试运行没问题,偶尔时候长度长了一些,就可能造成 缓冲区 数组越界写入,改写其他内存。
另外,随着计数数字的增大,其转换为字符串所占的字节数也越来越多,存在溢出隐患。
5、标准C库用法错误
标准C库并不是一个容错性,健壮性很好的函数库,对其的调用时需要非常注意输入参数,通常要对输入参数做一些异常判断,才能保证C库函数运行正常。而C语言又是函数式语言,其代码通常都离不开对标准C库的调用,输入参数检查多,代码冗长,编程效率低, 新人不喜欢。 新人通常喜欢高效,不做任何检查,直接调用C库,因为C库内又没有容错处理,所以时常导致各种异常发生,这也就是C语言不好学习,不好掌握的原因之一,也是新人很多时候编码没有埋雷多的原因之一。例如:
短整数类型 年=2025;
字节类型 月=1,日=12;
printf("缺省年月日:%d-%d-%d, 标志=%x\n",年,月,日,标志 );// 缺省年月日:2025-1-12
printf("请输入当前日期:" );
scanf("%d-%d-%d",&年,&月,&日); //用法有问题,参数类型不匹配。
上述示例代码中最后一行的用法就不正确,格式字符 %d 会将后面提供的 地址参数作为一个整数空间进行填充,而其空间定义的是字节类型或短整数类型,都比整数类型所占空间小,导致写入溢出,可能改写临近内存空间的数值。这种写法并不避免出错或异常,这还和处理器的字节序,编译优化处理相关。即使当前测试运行不出错,不保证未来代码修改,变量定义语句前后增加其他变量之后是否还能运行正常。 正确的用法应该是:
整数类型 年=2025;
整数类型 月=1,日=12;
printf("缺省年月日:%d-%d-%d, 标志=%x\n",年,月,日,标志 );
printf("请输入当前日期:" );
scanf("%d-%d-%d",&年,&月,&日);
此外还有 strcpy(目标地址,源地址); 如果源地址是0地址,就会出现非法地址访问错误。