项目最的出了几次运营事故,都是因为使用自乘、自加、自減运算,错改了非局部变量,导致将用户数据写溢出,最终只能进行回档处理。先给大家展示一下,漏出bug的代码吧。
代码展示
代码1
/*
* 给每个用户补偿固定值的金币数
*/
int main(int argc, char ** argv)
{
if (argc < 2)
{
printf("Usage:%s addmoney", argc[0]);
return -1;
}
/* 取出每个用户需要补偿的金币数 */
int addmoney = atoi(argv[1]);
int id = 0;
while (1)
{
/* 即出用户ID*/
id = GetUserId();
if (id <= 0)
{
break;
}
/* 补偿*/
addmoney += GetUserMoney(id);
SetUserMoney(addmoney);
}
return 0;
}
作者本意是给每个用户补偿固定值的金币数,即如果addmoney为500,那每个用户都加500,但这里因为把addmoney作了自加操作,结果后面的使用成了累加,如果这里合理的使用const并考虑到变量的作用域,就不会出错。修改后代码如下:
[cpp] view plain copy
/* 取出每个用户需要补偿的金币数 */
constint ADDMONEY = atoi(argv[1]);
while (1)
{
/* 即出用户ID*/
int id = GetUserId();
if (id <= 0)
{
break;
}
/* 补偿*/
int currentmoney = GetUserMoney(id);
currentmoney += ADDMONEY;
SetUserMoney(currentmoney);
}
代码2
[cpp] view plain copy
/*
* 给用户加上指定的金币数
* 金币数=金币基数*倍数, 基数保存在MapAddMoney中,倍数保存在MapMulti中,都使用id的低三位做key
*/
int AddMoney(int id)
{
int key = id%1000;
int &money = CMoneyConfig::MapAddMoney[key];
int &multi = CMoneyConfig::MapMulti[key];
money *= multi;
return AddMoney(money);
}
问题总结
其实这里的问题,通过良好的编程习惯是可以得到规避的,我总结了几点:
1. 使用局部变量,保证变量的最小作用域。
2. 对于明确不需要修改的变量,无论是传入参数、局部变量或返回值,加上const。
3. 慎用指针和引用,接口返回指针和引用时,加上const,防止调用者误操作。
4. 避免直接使用全局变量和类静态变量,类静态变量一定要通用带有const的接口返回,全局变量一样。
5. 对接口进行白盒测试,跑N轮,保证输出合法性。
良好的编程习惯和编程风格是项目成功的关键,不要为了高效而写晦涩的代码, 因为对项目而言,可读性和可维护性远比性能重要。最后重申一下本文的主旨“多使用const, 慎用自运算”!