30. Visual C++ and wprintf() function
The fragment is taken from Energy Checker SDK. The code contains an error that PVS-Studio analyzer diagnoses in the following way: V576 Incorrect format. Consider checking the second actual argument of the 'wprintf' function. The pointer to string of wchar_t type symbols is expected.
int main(void) { ... char *p = NULL; ... wprintf( _T("Using power link directory: %s\n"), p ); ... }
Explanation
Note: The first error is in the usage of _T for specifying a string in wide-character format. To use L prefix will be the correct variant here. However this mistake is not a crucial one and is not of a big interest to us. The code simply won't be compiled if we don't use a wide-character format and _T will expand into nothing.
If you want a wprintf() function to print a char* type string, you should use "%S" in the format string.
Many Linux programmers don't see where the pitfall is. The thing is that Microsoft quite strangely implemented such functions as wsprintf. If we work in Visual C++ with the wsprintf function, then we should use "%s" to print wide-character strings, at the same time to print char* strings we need "%S". So it's just a weird case. Those who develop cross platform applications quite often fall into this trap.
Correct code
The code I give here as a way to correct the issue is really not the most graceful one, but I still want to show the main point of corrections to make.
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
Recommendation
I don't have any particular recommendation here. I just wanted to warn you about some surprises you may get if you use functions such as wprintf().
Starting from Visual Studio 2015 there was a solution suggested for writing a portable code. For compatibility with ISO C (C99), you should point out to the preprocessor a _CRT_STDIO_ISO_WIDE_SPECIFIERS macro.
In this case the code:
const wchar_t *p = L"abcdef"; const char *x = "xyz"; wprintf(L"%S %s", p, x);
is correct.
The analyzer knows about _CRT_STDIO_ISO_WIDE_SPECIFIERS and takes it into account when doing the analysis.
By the way, if you turn on the compatibility mode with ISO C (the _CRT_STDIO_ISO_WIDE_SPECIFIERS macro is declared), you can get the old behavior, using the specifier of "%Ts" format.
In general the story about the wide - character symbols is quite intricate, and goes beyond the frames of one short article. To investigate the topic more thoroughly, I recommend doing some reading on the topic: