Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Starting at what version of Visual Studio is vsnprintf mostly standard-conformant?

According to Microsoft's documentation for vsnprintf, that function is part of the C(++) Run-Time Library since at least the 2003 edition of Visual Studio.

int vsnprintf( char *buffer,        // Storage location for output
               size_t count,        // Maximum number of characters to write
               const char *format,  // Format specification
               va_list argptr )     // Pointer to list of other arguments

I'm asking: for which versions of Visual Studio is the vsnprintf implementation of the bundled C(++) RTL for x86 and x64 in conformance with the C99 standard (ISO/IEC 9899:1999), assuming

  • #define _CRT_SECURE_NO_WARNINGS is performed before #include <stdio.h>, which is required for modern versions of Visual Studio RTL;
  • if count is greater than zero, then buffer is a pointer to (at least) count writable characters;
  • format is not NULL and conforms to Microsoft's Format Specification syntax as applicable to the particular version of the RTL;
  • the value of count and the number of characters to produce are both small enough to fit type int;

and we want conformance to include (beside basic functionality for nominal input) these requirements (implied by the standard's specification of snprintf, which vsnprintf references):

  1. not producing undefined behavior (including invoking Microsoft's invalid parameter handler) under the above assumptions;
  2. returning the length to be written (not including terminating null character) when buffer==NULL and count==0, thus allowing pre-flight to determine the length of the output;
  3. always padding the output string with a terminating null character when buffer!=NULL and count>0 and the result returned is non-negative, including for truncated output due to small count.

Note following comment: I'm willing to admit the lack of restrict qualifiers as still within allowance for mostly standard-conformant.


The documentation leaves conformance ambiguous with respect to (3.); the implementation bundled with Visual Studio Community 2015 is fine as far as I can tell, but not all are.

If there is room at the end (that is, if the number of characters to write is less than count), the buffer will be null-terminated.

The documentation also has wording unambiguously implying that vsnprintf is not in conformance with the C99 standard with respect to (1.) and (2.) when buffer==NULL and count==0 ; but theses parts of the documentation seemingly turn out to be wrong:

if the number of characters to write is greater than count, these functions return -1 indicating that output has been truncated.

If buffer or format is NULL, or if count is less than or equal to zero, these functions invoke the invalid parameter handler


Test code:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdarg.h>

int f( char *buffer,
       size_t count,
       const char *format,
       ...
     )
{
    va_list vArgs;
    int vRes;
    va_start(vArgs, format);
    vRes = vsnprintf( buffer, count, format, vArgs);
    va_end(vArgs);
    return vRes;
}

int main(void)
{
    char vBuf[6];
    int j, count;
#ifdef _MSC_VER
    printf("_MSC_VER = %ld\n",(long)(_MSC_VER));
#else
    printf("_MSC_VER is undefined\n");
#endif
    printf("f(NULL,0,\"%%d\",777):%3d\n", f(NULL,0,"%d",777));
    for(count=0 ;count<=sizeof(vBuf); ++count)
    {
        for(j=0; j<sizeof(vBuf)-1; ++j)
            vBuf[j] = '!';
        vBuf[j] = 0;
        j =  f(vBuf,count,"%d",777);
        printf("f(vBuf,%d,\"%%d\",777):%3d  vBuf: \"%s\"\n",count,j,vBuf);
    }
    return 0;
}

giving under my install of Visual Studio Community 2015

_MSC_VER = 1900
f(NULL,0,"%d",777):  3
f(vBuf,0,"%d",777):  3  vBuf: "!!!!!"
f(vBuf,1,"%d",777):  3  vBuf: ""
f(vBuf,2,"%d",777):  3  vBuf: "7"
f(vBuf,3,"%d",777):  3  vBuf: "77"
f(vBuf,4,"%d",777):  3  vBuf: "777"
f(vBuf,5,"%d",777):  3  vBuf: "777"
f(vBuf,6,"%d",777):  3  vBuf: "777"

and under some install of Visual Studio 2008 (I believe SP1 + PSDK 7.1)

_MSC_VER = 1500
f(NULL,0,"%d",777):  3
f(vBuf,0,"%d",777): -1  vBuf: "!!!!!"
f(vBuf,1,"%d",777): -1  vBuf: "7!!!!"
f(vBuf,2,"%d",777): -1  vBuf: "77!!!"
f(vBuf,3,"%d",777):  3  vBuf: "777!!"
f(vBuf,4,"%d",777):  3  vBuf: "777"
f(vBuf,5,"%d",777):  3  vBuf: "777"
f(vBuf,6,"%d",777):  3  vBuf: "777"

Notice the lack of terminating null character in particular for count==3, even though the output is positive.

like image 622
fgrieu Avatar asked Jan 25 '16 15:01

fgrieu


1 Answers

The very page you mention now gives the answer:

Beginning with the UCRT in Visual Studio 2015 and Windows 10, vsnprintf is no longer identical to _vsnprintf. The vsnprintf function complies with the C99 standard; _vnsprintf is retained for backward compatibility with older Visual Studio code.

And your output is consistent with _vsnprintf:

Both _vsnprintf and _vsnwprintf functions return the number of characters written if the number of characters to write is less than or equal to count; if the number of characters to write is greater than count, these functions return -1 indicating that output has been truncated.

like image 154
Michael Doubez Avatar answered Oct 12 '22 06:10

Michael Doubez