Visual C++ and wprintf() function

The fragment is taken from Energy Checker SDK. The code contains an error that 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.

16d40s

 

Correct code

The code we give here as a way to correct the issue is really not the most graceful one, but we 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

We don’t have any particular recommendation here. We 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, we recommend doing some reading on the topic:

Written by Andrey Karpov.
This error was found with PVS-Studio static analysis tool.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s