我过去在代码中多次使用过这种类型的约定:
strcpy ( cTmpA, "hello" );
sprintf ( cTmpA, "%s world", cTmpA );
最近我将我的旧C编译器切换到Visual Studio 2005,发现我得到了一个由上面的代码产生的乱码字符串。然后我想到,sprintf()的行为可能没有严格定义,其中一个输入与输出字符串匹配。
以上代码是否有效K& R C?如果没有,我如何找到代码中发生sprintf()调用的所有地方?
答案 0 :(得分:20)
虽然它是有效的K& R C,但您可能想要知道它是否有效POSIX - 请参阅sprintf Specification。我们读到:
如果由于调用sprintf()或snprintf()而重叠的对象之间发生复制,则结果是未定义的。
答案 1 :(得分:9)
sprintf()
的大多数实现都不复制格式字符串,而是在您传递的字符串中使用指针。如果格式和输出指向同一个内存,那将导致奇怪的结果。
你应该使用snprintf()
来保护你免受缓冲区溢出。
要查找所有呼叫,请将#define sprintf +++
放入公共标头查找并重新编译所有源。这应该给你一个错误列表以及文件名和行号:)或者使用你的IDE的递归搜索。
如果要将此列表修剪为两个参数使用相同指针的列表,请使用此宏:
#define sprintf(output,format,...) check_sprintf(__FILE__,__LINE__,output,format,....)
请注意,并非所有编译器都支持使用varargs的宏。然后定义一个新函数check_sprintf
:
int check_sprintf (char*filename,int line,char*output,char*format,...) {
va_list args;
int len;
if(output==format) {
fprintf(stderr,
"Output and format are the same at %s:%d", filename, line);
abort();
}
va_start (args, format);
len = vsprintf (output, format, args);
va_end (args);
return len;
}
[编辑]我刚看到你在谈论输出和第一个参数。您可以重用上面的代码并调用va_arg()
来获取第一个参数并在比较中使用它。