# V576. Incorrect format. Consider checking the Nth actual argument of the 'Foo' function.

The analyzer has detected a potential issue with the application of formatted output functions ('printf', 'sprintf', 'wprintf', etc.). The formatting string doesn't correspond with actual arguments passed into the function.

Let's review a simple example:

    int A = 10;
    double B = 20.0;
    printf("%i %i\n", A, B);

According to the formatting string the 'printf' function is expecting two actual arguments of the 'int' type. However, the second argument's value is of the 'double' type. Such an inconsistency leads to undefined behavior of a program. For example, it can lead to the output of senseless values.

The correct version:

    int A = 10;
    double B = 20.0;
    printf("%i %f\n", A, B);

It's possible to cite countless examples of 'printf' function's incorrect use. Let's review some of the typical examples that are the most frequently encountered in applications.

Address printout

The value of a pointer is quite commonly printed using these lines:

    int *ptr = new int[100];
    printf("0x%0.8X\n", ptr);

This source code is invalid because it will function properly only on systems which have their pointer size equal to size of the 'int' type. For example, in Win64, this code will print only the low-order part of the 'ptr' pointer. The correct version:

    int *ptr = new int[100];
    printf("0x%p\n", ptr);

The analyzer has detected the potential issue with an odd value being passed as the function's actual argument.

Unused arguments

You can often encounter function calls in which some of these function's arguments are being unused.

For example:

    int nDOW;
    #define KEY_ENABLED "Enabled"
    ...
    wsprintf(cDowKey, L"EnableDOW%d", nDOW, KEY_ENABLED);

It is obvious that the 'KEY_ENABLED' parameter is unnecessary here or the source code should look like this:

    wsprintf(cDowKey, L"EnableDOW%d%s", nDOW, KEY_ENABLED);

Insufficient number of arguments

A little more dangerous is the situation in which the number of arguments passed to the function is less than necessary. This can easily lead to the memory access error, buffer overflow, or senseless printout. Let's review an example of memory allocation function taken from a real-life application:

    char* salloc(register int nbytes)
    {
        register char* p;
        p = (char*) malloc((unsigned)nbytes);
        if (p == (char *)NULL)
        {
            fprintf(stderr, "%s: out of memory\n");
            exit(1);
        }
        return (p);
    }

If 'malloc' returns 'NULL', the program will not be able to report the shortage of memory and to be terminated correctly. It instead will be terminated emergently, and it will output the senseless text. In any case, such a behavior will complicate analysis of the program's inoperability.

Confusion with signed/unsigned

Developers often employ the character printing specificator ('%i' for example) to output the variables of the 'unsigned' type. And vice versa. This error usually is not critical and is encountered so often than it has a low priority in analyzer. In many cases such source code works flawlessly and fails only with large or negative values. Let us examine the code which is not correct, but successfully works:

    int A = 10;
    printf("A = %u\n", A);
    for (unsigned i = 0; i != 5; ++i)
      printf("i = %d\n", i);

Although there is an inconsistency here, this code outputs correct values in practice. Of course, it's better not to do this and to write correctly:

    int A = 10;
    printf("A = %d\n", A);
    for (unsigned i = 0; i != 5; ++i)
      printf("i = %u\n", i);

The error will manifest itself in case there are large or negative values in the program. An Example:

    int A = -1;
    printf("A = %u", A);

Instead of "A=-1" string the program will print "A=4294967295". The correct version:

    printf("A = %i", A);

Wide character strings

Visual Studio has one displeasing feature when it interprets the string format in a non-standard way to print wide characters. Therefore, the analyzer can diagnose errors in code like the following sample:

    const wchar_t *p = L"abcdef";
    wprintf(L"%S", p);

In Visual C++, '%S' is meant to be used to print a string of the 'const char *' type, so from its viewpoint, the correct version of the code above should look like this:

    wprintf(L"%s", p);

Starting with Visual Studio 2015, the developers offer a solution to this issue for the sake of compatibility. To make your code compatible with ISO C (C99), you need to specify the _CRT_STDIO_ISO_WIDE_SPECIFIERS macro for the preprocessor.

In that case, the code:

    const wchar_t *p = L"abcdef";
    wprintf(L"%S", p);

will be treated as correct.

PVS-Studio knows about the '_CRT_STDIO_ISO_WIDE_SPECIFIERS' macro and takes it into account when performing the analysis.

By the way, if you have the ISO C compatibility mode enabled (i.e. declared the '_CRT_STDIO_ISO_WIDE_SPECIFIERS' macro), you can restore the old-type conversion in certain places by using the '%Ts' format specifier.

This story with wide characters is quite complicated and is outside the scope of this documentation. To figure it all out, see the following resources:

- Bug 1121290 - distinguish specifier s and ls in the printf family of functions
- Visual Studio swprintf is making all my %s formatters want wchar_t * instead of char *
- Update. In 2019, an article appeared which explains why confusion takes place: The sad history of Unicode printf-style format specifiers in Visual C++.

Additional diagnostic setup

Use custom annotations to specify the names of user-declared functions for which the format check should be performed. You can read more about this here.

Additional references:

- Wikipedia. Printf.
- MSDN. Format Specification Fields: printf and wprintf Functions.

This diagnostic is classified as:

CWE-628 CWE-685 CWE-686 CWE-687 CERT-DCL11-C CERT-FIO47-C

You can look at examples of errors detected by the V576 diagnostic.