12

I am currently converting the code of one of our Linux libraries to a Windows DLL.

Within this library I have a function which takes the last parameters in a printf-way (format string, then ellipsis). Within this function I use vsnprintf to format the supplied arguments. Since I want to know whether I can cram the final string into a small buffer or if I'd have to allocate memory for it, I am interested in determining the "would-be-length" of the formatted string.

To do this, I am currently using vsnprintf like this (made up example code, obviously):

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>

void foo(const char* fmt, ...)
{
   int len = 0;
   va_list ap;

   va_start(ap, fmt);
   len = vsnprintf(0, 0, fmt, ap);
   printf("len = %d\n", len);
   va_end(ap);
}

int main(void)
{
   foo("12345%s", "67890");
   exit(0);
}

This usage is covered by the Open Group Base Specifications Issue 6:

vsnprintf(char *restrict s, size_t n, const char *restrict format, va_list ap)

The [...] vsnprintf() [...] functions shall be equivalent to [...] snprintf().

snprintf(char *restrict s, size_t n, const char *restrict format, ...)

If the value of n is zero on a call to snprintf(), nothing shall be written, the number of bytes that would have been written had n been sufficiently large excluding the terminating null shall be returned, and s may be a null pointer.

The problem arised as I was compiling this code on the Windows-System (Visual Studio 2010) with /analyze on. The compiler/analyzer gave me the following:

test.c(11) : warning C6309: Argument '1' is null: this does not adhere to function specification of 'vsnprintf'

test.c(11) : warning C6387: 'argument 1' might be '0': this does not adhere to the specification for the function 'vsnprintf': Lines: 7, 8, 10, 11

A quick look at the MSDN entry for vsnprintf gave me this:

If buffer or format is NULL, or if count is less than or equal to zero, these functions invoke the invalid parameter handler, as described in Parameter Validation. If execution is allowed to continue, these functions return -1.

Curious enough, the above sample works nonetheless on Windows "as expected" (i.e. it returns to me the count of the characters that would be written).

But since I don't want this to rely on something unspecified I'd like to know if there is a better, official way of achieving the same, without having to hope that this won't break in some future release.

Thanks for your time!

lx.
  • 2,317
  • 1
  • 22
  • 32
  • Enable C99 on your compiler, it must be available I think. Or uses http://www.ijs.si/software/snprintf/ – Jaffa Dec 13 '11 at 11:51
  • 1
    @Geo Not on Microsoft's compilers. – Artefacto Dec 13 '11 at 11:53
  • 1
    This seems more a documentation problem than anything else. The debug crt sources in vs9 explicitly allow for this situation: `_VALIDATE_RETURN( (count == 0) || (string != NULL), EINVAL, -1 );` and below `if ( string==NULL) return(retval);`, so they thought about the case where count == 0 and string == NULL. – Artefacto Dec 13 '11 at 12:06
  • @Artefacto: Thanks for looking that up. I'll write a mail to the MSDN documentation folks. Maybe they can clarify their wording. – lx. Dec 13 '11 at 12:15
  • 1
    Consider using _vscprintf() instead. – Hans Passant Dec 13 '11 at 13:28
  • @Hans: Thanks! That's the kind of alternative I was looking for (although I have to add ugly ifdefs now ;)). If you'd like, you can make a post so I can flag the question as answered. – lx. Dec 13 '11 at 13:39
  • Go ahead and make your own post from it and mark it answered. – Hans Passant Dec 13 '11 at 13:45

2 Answers2

12

Answer provided in comments by Hans Passant:

The documented _vscprintf provides this functionality on Windows, so relying on "unspecified behaviour" is not necessary.

lx.
  • 2,317
  • 1
  • 22
  • 32
  • 3
    It's actually not "unspecified behavior", it's behavior contrary to the specification. – Artefacto Dec 13 '11 at 16:18
  • The MSDN `_vscprintf` doc link in this answer is out-of-date. Here is an updated link for 2020: https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/vscprintf-vscprintf-l-vscwprintf-vscwprintf-l?view=vs-2019 – Jeff Wheeler Jul 13 '20 at 21:21
3

Take this example from the manual page of snprintf:

Here is how to allocate a buffer in order to fit your string.

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>

char *
make_message(const char *fmt, ...)
{
    int n;
    int size = 100;     /* Guess we need no more than 100 bytes. */
    char *p, *np;
    va_list ap;

   if ((p = malloc(size)) == NULL)
        return NULL;

   while (1) {

       /* Try to print in the allocated space. */

       va_start(ap, fmt);
        n = vsnprintf(p, size, fmt, ap);
        va_end(ap);

       /* If that worked, return the string. */

       if (n > -1 && n < size)
            return p;

       /* Else try again with more space. */

       if (n > -1)    /* glibc 2.1 */
            size = n+1; /* precisely what is needed */
        else           /* glibc 2.0 */
            size *= 2;  /* twice the old size */

       if ((np = realloc (p, size)) == NULL) {
            free(p);
            return NULL;
        } else {
            p = np;
        }
    }
}
INS
  • 10,594
  • 7
  • 58
  • 89
  • I know of this possibility. However I want to avoid mallocs when necessary, so this is not an exact replacement for what I was looking for. Thanks for looking that example up though :) – lx. Dec 13 '11 at 13:34
  • The best answer for this (v)snprintf on Visual studio and its difference with posix is answered here with a good sample code : http://stackoverflow.com/questions/2915672/snprintf-and-visual-studio-2010 – Vincent Ricosti May 23 '14 at 07:28