1 现象:问题描述
CAS业务后台处理程序处理非法操作请求码时,打印的错误日志不全。在输入码流文件中手工构造一个尾结束符'\0'作为operateflag的值,casagent写日志不完整,如下:
[14:17:02] [ERROR] CASUpdate::Process(), user(13903000004), OperateFlag(
2 关键过程:根本原因分析
检查打印此日志部分的代码:
int TCASUpdate::Process(TBusiControlList & BusiCtrlList)
{
…
switch(BusiCtrlList.m_cOperateFlag)
{
case '2': //删除标识
if(0!=BusiCtrlRec.Delete(BusiCtrlList.m_szMsisdn)
|| 0!=BusiCtrlListRec.Delete(BusiCtrlList.m_szMsisdn))
{
…
}
…
case '1': //更新标识
rv = BusiCtrlRec.Select(BusiCtrlList.m_szMsisdn,oldtypeset);
…
default:
EXEC SQL rollback work;
WriteLog(LOG_ERROR, "CASUpdate::Process(), user(%s), OperateFlag(%c) is illegal!\n", BusiCtrlList.m_szMsisdn, BusiCtrlList.m_cOperateFlag);
return 2;
}
}
WriteLog 函数中调用了如下代码:
va_list tVal;
szItem[MAX_LEN_LOGITEM] = 0;
…
// 将变参按格式输出到日志项字符串中
vsnprintf(szItem, MAX_LEN_LOGITEM, sFormat, tVal);
将格式化字符串打印到字符数组变量中,然后打印到文件中,除了接收可变列表va_list 作为参数外,vsnprintf和sprintf功能相同。由于%c 对应的变量为'\0',可以看到打印到字符串变量szItem中包含了'\0',从而使得后面的内容看不到了('\0'作为了字符串的结束符)。
可以使用如下代码验证:
char szTmp[100] = "abc";
char c = 0;
sprintf(szTmp,"%c is number 0", c);
printf("%s\n",szTmp);
如上代码打印将时一个空行,没有打印出包含 "is number 0"的内容。
但我们直接使用printf时不会出现这样的问题:
char c = 0;
printf("'%c' is number 0", c);
如上代码打印出的是 "'' is number 0" .
3 结论:解决方案及效果
实际应用中操作码都是从字符中解析出的,因此不会出现\0的情况,但为了程序的健壮性。增加异常处理:
if (0 == BusiCtrlList.m_cOperateFlag)
{
WriteLog(LOG_ERROR, "CASUpdate::Process(), user(%s), OperateFlag =0x00 is illegal!\n", BusiCtrlList.m_szMsisdn)
}
else
{
WriteLog(LOG_ERROR, "CASUpdate::Process(), user(%s), OperateFlag(%c) is illegal!\n", BusiCtrlList.m_szMsisdn, BusiCtrlList.m_cOperateFlag);
}
4 经验总结:预防措施和规范建议
sprintf 使用%c 格式打印单个字符时需要注意,如果字符变量为'\0'时,将在打印的字符串变量中赋一个字符串结束符'\0',导致后面的打印被截断了。
5 备注
6 考核点
sprintf使用%c打印char 变量时要注意变量为'\0'的情况。
7 试题
如下代码打印的结果是:B
char szTmp[100] = "abc";
char c = 0;
sprintf(szTmp,"'%c' is number 0", c);
printf("%s\n",szTmp);
A) '' is number 0
B) '
C) abc
D) '0' is number 0
阅读(357) | 评论(0) | 转发(0) |