有个程序的bug昨天晚上查了一晚上。
在程序中我使用了一个支持可变形参的logger。
可变形参就是C语言中的printf(const char* fmt, ...)这类的函数。
但是在程序运行的时候core dump。
bug是这样的,某个文件里面有一个字符串是这样的,"40%slik60%milk"。
读进来之后就成了string s("40%slik60%milk");
然后在某个debug的语句中,有LogDebug(s);
你就发现显示的结果是莫名其妙的。
原因见如下例子:
#include <iostream>
#include <string>
#include <stdio.h>
using namespace std;
int main()
{
const char *a = "a[helloworld]\n";
printf(a);
//const char *b = "b[%s]\n";
const char *b = "40%smilk60%silk";
printf(b);
return 0;
}
运行的结果如下:
a[helloworld]
4040404040404040milk60ilk
其实这个程序在编译的时候编译器就会给出警告(因为这里的字符串是写死在程序里的,如果是运行时载入的编译器就无能为力):
warning: format not a string literal and no format arguments [-Wformat-security]
其实本质就是一个原因,可变形参是
va_list和vsnprintf
之类的宏定义来实现的,通过运行时指针的偏移来获得函数的若干个可变形参。
但是这里的隐患就是,当printf的第一个形参format里面含有%的时候,即使printf只有一个形参,
在运行时vsnprintf之类的东西都会去通过指针的偏移来获取函数可变形参,这就会出现未定义的行为,就像刚才的结果。
突然间就明白了为什么C++的cin没有支持这种可变形参的函数形式,我想可能就是有这类隐患的原因吧。
对于此类问题,暂时我能想到的解决办法就是在使用这类可变形参的函数时候一定要小心。
即最好使用至少两个形参的形式。
如下:
#include <iostream>
#include <string>
#include <stdio.h>
using namespace std;
int main()
{
const char *a = "a[helloworld]\n";
printf("%s",a);
//const char *b = "b[%s]\n";
const char *b = "40%smilk60%silk\n";
printf("%s",b);
//cout<<b<<endl;
return 0;
}