翻译《有关编程、重构及其他的终极问题?》——30.Visual C++和wprintf()函数

翻译《有关编程、重构及其他的终极问题?》——30.Visual C++和wprintf()函数

标签(空格分隔):翻译 技术 C/C++
作者:Andrey Karpov
翻译者:顾笑群 - Rafael Gu
最后更新:2017年07月20日


30.Visual C++和wprintf()函数

下面的代码摘自Energy Checker SDK。这些代码包含的错误被PVS-Studio分析器诊断为:V576 Incorrect format. Consider checking the second actual argument of the ‘wprintf’ function. The pointer to string of wchar_t type symbols is expected(译者注:请检查wprintf函数的第二个参数,这里期望的是指向wchar_t类型的指针)。

int main(void) {
  ...
  char *p = NULL;
  ...
  wprintf(
    _T("Using power link directory: %s\n"), 
    p
  );
  ...
}

解释
注意:第一个错误是使用了_T来指定字符串为宽字符格式,其实使用L才是正确的用法。但这个错误并不是至关重要的,而且我们这里也是对这个错误感兴趣。在这里,如果我们不使用宽字符串,我们的代码也会简单的不能被编译通过,_T不会有任何展开。

如果你想使用wprintf()函数打印char *类型的字符串,你应该在格式化字符串中使用%S。

很多Linux程序员看不到陷阱在哪里,这是因为只有微软如此奇怪的实现了这么一个wsprintf函数。如果我们在Visual C++中使用wsprintf函数,我们应该使用%s来打印宽字符的字符串,同时如果我们需要打印char *字符串时需要使用%S。所以这只是一个有些奇怪的特例,但也因此那些开发阔平台应用的经常掉到这个陷阱里。

正确的代码
我在下面给出的纠正后的代码并不是最优雅的,但我依然想特别强调一下改正的主要点在哪里。

char *p = NULL;
...
#ifdef defined(_WIN32)
wprintf(L"Using power link directory: %S\n"), p);
#else
wprintf(L"Using power link directory: %s\n"), p);
#endif

建议
在这方面我没有任何特别的建议。我只是想警告你,如果你使用一些类似wprintf()的函数,你会被回馈一些惊喜:)。

从Visual Studio 2015开始,微软已经开始建议一种可移植代码的解决方案。为了和ISO C(C99)兼容,你应该指明预编译宏_CRT_STDIO_ISO_WIDE_SPECIFIERS。

在这种情况下,下面代码是正确的:

const wchar_t *p = L"abcdef";
const char *x = "xyz";
wprintf(L"%S %s", p, x);

PVS-Studio的分析器是知道_CRT_STDIO_ISO_WIDE_SPECIFIERS的,并且会在进行代码分析时考虑这个因素。

顺便说一下,如果你打开了ISO C的兼容模式(声明了_CRT_STDIO_ISO_WIDE_SPECIFIERS宏),你能恢复到旧的行为,即使用%Ts格式。

总之关于宽字符的字符串的故事时复杂的,不是一篇段文章可以描述清楚的。如果你想了解更多更详细的信息,我建议你可以读读一下的文章:

猜你喜欢

转载自blog.csdn.net/headman/article/details/75570689