代码段1
#include <stdint.h>
typedef struct{
int member1;
void (*cb_member)(void* arg);
}xxx_init_stru;
extern void xxx_init(xxx_init_stru* init);
void func1(void)
{
xxx_init_stru* init;
init->member1 = 0;
init->cb_member = NULL;
xxx_init(init);
}
代码段2
#include <stdint.h>
#include "fsm.h"
static void event_cb(uint8_t ev, uint16_t* arg)
{
switch(*arg){
case 1:
run1();
break;
case 2:
run2();
case 3:
run3();
break;
default:
break;
}
}
void func1(void){
extern uint16_t GetXxxInfo(void);
uint16_t val;
val = GetXxxInfo();
FsmEventCx(0, (FsmFunc*)event_cb, &val);
}
代码段3
#include <stdint.h>
typedef struct{
int member;
void (*cb_member)(void* arg);
}xxx_init_stru;
extern void xxx_init(xxx_init_stru* init);
void func1(void)
{
xxx_init_stru init;
init.member1 = 0;
xxx_init(&init);
}
代码段4
unsigned int xxx(const unsigned char* data, size_t len)
{
unsigned int sum = 0;
const unsigned char * end = data + len;
while(data != end)
{
if(*data == 10){
continue;
}
sum += *data++;
}
return sum;
}
代码段5
#include <ctype.h>
#include <stdio.h>
int print_hex(const void* data, size_t len)
{
char src_chr[17] = {0};
const char *start = data;
size_t align_16B = (len & 0x0F) ? (((len >> 4) + 1) << 4) : len;
for(size_t i = 0; i < align_16B; ++i){
if(i < len){
src_chr[i & 0x0F] = isgraph(start[i]) ? start[i] : '.';
printf(" 02X", start[i]);
}
else{
src_chr[i & 0x0F] = 0;
printf(" ");
}
if(i & 0x0F == 0x0F)
{
printf("\t%s\r\n", src_chr);
}
}
}
代码段6
err_code_t err_code = ERR_CODE_SUCCESS;
#define err err_code
err_code_t func1(void)
{
do{
err_code_t err = run1();
if(err != ERR_CODE_SUCCESS){
break;
}
err = run2();
if(err != ERR_CODE_SUCCESS){
break;
}
err = run3();
if(err != ERR_CODE_SUCCESS){
break;
}
err = run4();
}while(0);
return err;
}
代码段7
#define INVALID_VAL (-1)
#define VALID_VERIFY(a)\
if((a) == INVALID_VAL)\
return INVALID_VAL
bool get_val(int &a);
int func1(void)
{
int val;
if(get_val(&val))
VALID_VERIFY(a);
else
return INVALID_VAL;
run(a);
}
代码段8
- 文件1
int g_status = STATUS_IDEL;
- 文件2
extern int g_status = STATUS_IDEL;
代码段1分析
#include <stdint.h>
typedef struct{
int member1;
void (*cb_member)(void* arg);
}xxx_init_stru;
extern void xxx_init(xxx_init_stru* init);
void func1(void)
{
xxx_init_stru* init; //指针指向的内容没有分配内存,指向位置未知,程序可能运行正常,但是可能导致其他代码出现工作异常。未注意野指针现象,异常很难排查。
init->member1 = 0;
init->cb_member = NULL;
xxx_init(init);
}
- 问题分析
野指针问题,使用了为初始化的指针。指针指向的内容没有分配内存,指向位置未知,程序可能运行正常,但是可能导致其他代码出现工作异常。很难排查。在使用动态内存的时候更容易出现,如多个地方使用同一块动态内存,其中一个地方释放了该内存块,但是其他使用该内存的地方并不知道,继续对该内存进行操作,而该内存区域可能又被分配给其他地方使用,从而出现很难排查的错误。
- 问题预防
对于使用指针的地方一定要注意该指针指向的位置是否有分配内存(静态分配,动态分配)。对于使用全局相关的指针,且内存为动态分配的时候,在释放后一定要将该指针置空,且使用指针前一定要进行空指针判定。
代码段2分析
#include <stdint.h>
#include "fsm.h"
static void event_cb(uint8_t ev, uint16_t* arg)
{
switch(*arg){
case 1:
run1();
break;
case 2:
run2(); //没有break,执行完run2()后会立刻执行run3(),语法正确,编译器不会报错。如需某标签不需要break,应注释说明。
case 3:
run3();
break;
default:
break;
}
}
void func1(void){
extern uint16_t GetXxxInfo(void);
uint16_t val;
val = GetXxxInfo();
FsmEventCx(0, (FsmFunc*)event_cb, &val); //传递的指针是局部变量,该变量分配的是栈空间,函数退出后,可能被其他函数使用修改。
}
- 问题分析
- switch的case 2没有break,执行完run2()后会立刻执行run3(),该段语句语法正确,编译器不会报错和警告。
- FsmEventCx传递的参数指向的变量为局部变量,局部变量执行过程中分配的是栈空间,这部分内存会在程序执行过程中由程序动态分配,函数退出后,会被回收,再分配使用。
- 问题预防
1.对于该问题,在使用switch的时候,可以先将分支和break写完后,在补充各个分支的处理代码。比较不容易出现该错误。对于确实需要使用该特性的代码,应在代码中注释说明,否则应该视为bug。
2.对于使用指针传输局部变量等数据的时候,需要特别注意被调函数会在什么时候使用该指针传递的数据。如对于串口的发送数据函数,阻塞的接口可以使用而非阻塞的接口一般不能使用。
代码段3分析
#include <stdint.h>
typedef struct{
int member;
void (*cb_member)(void* arg);
}xxx_init_stru;
extern void xxx_init(xxx_init_stru* init);
void func1(void (*cb)())
{
xxx_init_stru init; //结构体没有初始化
init.member1 = 0;
xxx_init(&init); //没有对cb_member变量进行初始化,其值不确定。
}
- 问题分析
定义的局部变量没有初始化,且使用的时候也有成员没有赋值
- 问题预防
1.对于该问题,在使用switch的时候,可以先将分支和break写完后,在补充各个分支的处理代码。比较不容易出现该错误。对于确实需要使用该特性的代码,应在代码中注释说明,否则应该视为bug。
2.对于使用指针传输局部变量等数据的时候,需要特别注意被调函数会在什么时候使用该指针传递的数据。如对于串口的发送数据函数,阻塞的接口可以使用而非阻塞的接口一般不能使用。
代码段4分析
unsigned int xxx(const unsigned char* data, size_t len)
{
unsigned int sum = 0;
const unsigned char * end = data + len;
while(data != end)
{
if(*data == 10){
continue; //未执行data++,陷入死循环
}
sum += *data++;
}
return sum;
}
- 问题分析
使用continue时没有对判断的条件进行变换,导致程序工作异常。
- 问题预防
每次判断条件的改变都是一致的情况可以使用for循环进行处理。
代码段5分析
#include <ctype.h>
#include <stdio.h>
int print_hex(const void* data, size_t len)
{
char src_chr[17] = {0};
const char *start = data;
size_t align_16B = (len & 0x0F) ? (((len >> 4) + 1) << 4) : len;
for(size_t i = 0; i < align_16B; ++i){
if(i < len){
src_chr[i & 0x0F] = isgraph(start[i]) ? start[i] : '.';
printf(" 02X", start[i]);
}
else{
src_chr[i & 0x0F] = 0;
printf(" ");
}
if(i & 0x0F == 0x0F) //优先级问题, '==' 的优先级高于 '&'
{
printf("\t%s\r\n", src_chr);
}
}
}
- 问题分析
对优先级的判断有误,导致程序运行问题。
- 问题预防
对于没有把握的优先级,可以使用小括号来处理,同时也能更清晰的表达表达式的含义
代码段6分析
err_code_t err_code = ERR_CODE_SUCCESS;
#define err err_code
err_code_t func1(void)
{
do{
err_code_t err = run1();
if(err != ERR_CODE_SUCCESS){
break;
}
err = run2();
if(err != ERR_CODE_SUCCESS){
break;
}
err = run3();
if(err != ERR_CODE_SUCCESS){
break;
}
err = run4();
}while(0);
return err;
}
- 问题分析
变量(宏)重名和作用域问题,函数内部的err在do循环结束后生命周期就结束了,但因为外部有重名的宏,导致编译器未报错。
- 问题预防
关注变量的作用域,对于宏建议全用大写,函数和变量名用小写,变量通过作用域添加标识等
代码段7分析
#define INVALID_VAL (-1)
#define VALID_VERIFY(a)\
if((a) == INVALID_VAL)\
return INVALID_VAL
bool get_val(int &a);
int func1(void)
{
int val;
if(get_val(&val))
VALID_VERIFY(a);
else // else 悬挂问题,此处else匹配的是宏展开后的if语句
return INVALID_VAL;
run(a);
}
- 问题分析
else和最近可匹配的if语句匹配,上述代码段的else最近可匹配的if是宏展开后的if语句,导致逻辑错误
- 问题预防
对于if语句,不管后续是否只有一个语句,建议都添加’{}’,划明作用域;复杂宏建议用‘do{}while(0)’包裹
代码段8分析
- 文件1
int g_status = STATUS_IDEL;
- 文件2
extern int g_status = STATUS_IDEL;
- 问题分析
外部声明全局变量不能初始化,否则将视为重新定义一个变量。编译器会产生警告,但是不会报错,
- 问题预防
extern 全局变量放到头文件中(初始化会在实际定义的变量的文件报重复定义的错误),少用或者不用全局变量,使用静态的全局变量,并提供接口修改变量。