2009-12-29 121 views
18

我已经在过去使用这种类型的会议多次在我的代码:sprintf()的输入和输出字符串可以相同吗?

strcpy (cTmpA, "hello"); 
sprintf (cTmpA, "%s world", cTmpA); 

最近我打开我的遗留的C编译器Visual Studio 2005中,发现我从上面的代码产生的乱码字符串。然后,我想到sprintf()的行为可能并非严格定义,其中一个输入与输出字符串匹配。

上述代码是否有效K & R C?如果没有,我怎么找到我的代码中发生这种类型的调用sprintf()的所有地方?

+2

重复http://stackoverflow.com/questions/1283354/is-sprintfbuffer-s-buffer-safe – Alon

回答

18

虽然它是有效的K & R C,你可能想知道它是否是有效的POSIX - 见sprintf Specification。我们阅读:

如果复制发生在因调用sprintf()或snprintf()而重叠的对象之间,则结果是未定义的。

8

sprintf()的大部分实现都不会复制格式字符串,而是在传递的字符串中使用指针。如果格式和输出指向相同的内存,那将导致奇怪的结果。

而且你应该真的使用snprintf()它可以保护你免受缓冲区溢出。

要查找所有呼叫,请将#define sprintf +++放入共同标题查找并重新编译所有来源。这应该会给你一个错误列表以及文件名和行号:)或者使用递归search'n'replace你的IDE。

如果要修剪下来这个列表到您使用了两个参数相同指针的,使用这个宏:

#define sprintf(output,format,...) check_sprintf(__FILE__,__LINE__,output,format,....) 

请注意,并非可变参数所有的编译器支持宏。然后定义一个新的功能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()以获取第一个参数并在比较中使用它。

+0

好主意使用宏替代来发现这种情况发生。我想到了这一点,但想不到一种方法来制造只在输出也是输入之一时导致编译时错误的宏。重新定义sprintf()的问题是,这只会在运行时显示,而我的项目足够大,以至于如果没有某种类型的自动化单元测试,我绝不会找到每条代码路径。 – Piers

+0

随着时间的推移,你会发现所有的地方,因为程序崩溃困难(而不是静静地产生越野车数据)。因此,从现在的单元测试开始,尽可能多地修复,然后在错误报告进入时修复其余部分。 –

相关问题