web-dev-qa-db-fra.com

snprintf et Visual Studio 2010

Je suis assez malheureux pour être coincé dans VS 2010 pour un projet. J'ai remarqué que le code suivant ne fonctionnait toujours pas à l'aide du compilateur non conforme aux normes:

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

int main (void)
{
    char buffer[512];

    snprintf(buffer, sizeof(buffer), "SomeString");

    return 0;
}

(échec de la compilation avec l'erreur: C3861: 'snprintf': identifiant introuvable)

Je me souviens que c'était le cas avec VS 2005 et je suis choqué de voir que cela n'a toujours pas été corrigé.

Est-ce que quelqu'un sait si Microsoft envisage de déplacer ses bibliothèques C standard en 2010?

99
Andrew

Short story: Microsoft a finalement implémenté snprintf dans Visual Studio 2015. Sur les versions antérieures, vous pouvez le simuler comme suit.


Version longue:

Voici le comportement attendu pour snprintf:

int snprintf( char* buffer, std::size_t buf_size, const char* format, ... );

Écrit au plus buf_size - 1 Caractères dans un tampon. La chaîne de caractères résultante sera terminée par un caractère nul, à moins que buf_size Ne soit égal à zéro. Si buf_size Est égal à zéro, rien n'est écrit et buffer peut être un pointeur nul. La valeur de retour est le nombre de caractères qui auraient été écrits avec un nombre illimité de buf_size, Sans compter le caractère nul final.

Les versions antérieures à Visual Studio 2015 n'avaient pas d'implémentation conforme. Il existe à la place des extensions non standard telles que _snprintf() (qui n'écrit pas de terminaison nulle en cas de dépassement de capacité) et _snprintf_s() (qui peut appliquer une terminaison nulle, mais renvoie -1 en cas de dépassement de capacité au lieu du nombre de caractères qui auraient été écrits).

Solution de secours suggérée pour VS 2005 et les versions ultérieures:

#if defined(_MSC_VER) && _MSC_VER < 1900

#define snprintf c99_snprintf
#define vsnprintf c99_vsnprintf

__inline int c99_vsnprintf(char *outBuf, size_t size, const char *format, va_list ap)
{
    int count = -1;

    if (size != 0)
        count = _vsnprintf_s(outBuf, size, _TRUNCATE, format, ap);
    if (count == -1)
        count = _vscprintf(format, ap);

    return count;
}

__inline int c99_snprintf(char *outBuf, size_t size, const char *format, ...)
{
    int count;
    va_list ap;

    va_start(ap, format);
    count = c99_vsnprintf(outBuf, size, format, ap);
    va_end(ap);

    return count;
}

#endif
85
Valentin Milea

snprintf ne fait pas partie de C89. C'est la norme seulement dans C99. Microsoft a pas de plan prenant en charge C99 .

(Mais c'est aussi standard en C++ 0x ...!)

Voir les autres réponses ci-dessous pour une solution de contournement.

32
kennytm

Si vous n'avez pas besoin de la valeur de retour, vous pouvez aussi simplement définir snprintf comme _snprintf_s

#define snprintf(buf,len, format,...) _snprintf_s(buf, len,len, format, __VA_ARGS__)
8
Stefan Steiger

Je crois que l’équivalent Windows est sprintf_s

3
Il-Bhima

Ffmpeg fournit un autre remplacement sûr de snprintf() et vsnprintf(). Vous pouvez commander le source ici (suggéré).

2
Marco Pracucci

J'ai essayé le code de @Valentin Milea mais j'ai des erreurs de violation d'accès. La seule chose qui a fonctionné pour moi a été l'implémentation d'Insane Coding: http://asprintf.insanecoding.org/

Plus précisément, je travaillais avec le code hérité de VC++ 2008. De la mise en œuvre de Insane Coding (peut être téléchargé à partir du lien ci-dessus), j'ai utilisé trois fichiers: asprintf.c, asprintf.h et vasprintf-msvc.c. D'autres fichiers étaient pour d'autres versions de MSVC.

[EDIT] Pour être complet, leur contenu est le suivant:

asprintf.h:

#ifndef INSANE_ASPRINTF_H
#define INSANE_ASPRINTF_H

#ifndef __cplusplus
#include <stdarg.h>
#else
#include <cstdarg>
extern "C"
{
#endif

#define insane_free(ptr) { free(ptr); ptr = 0; }

int vasprintf(char **strp, const char *fmt, va_list ap);
int asprintf(char **strp, const char *fmt, ...);

#ifdef __cplusplus
}
#endif

#endif

asprintf.c:

#include "asprintf.h"

int asprintf(char **strp, const char *fmt, ...)
{
  int r;
  va_list ap;
  va_start(ap, fmt);
  r = vasprintf(strp, fmt, ap);
  va_end(ap);
  return(r);
}

vasprintf-msvc.c:

#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include "asprintf.h"

int vasprintf(char **strp, const char *fmt, va_list ap)
{
  int r = -1, size = _vscprintf(fmt, ap);

  if ((size >= 0) && (size < INT_MAX))
  {
    *strp = (char *)malloc(size+1); //+1 for null
    if (*strp)
    {
      r = vsnprintf(*strp, size+1, fmt, ap);  //+1 for null
      if ((r < 0) || (r > size))
      {
        insane_free(*strp);
        r = -1;
      }
    }
  }
  else { *strp = 0; }

  return(r);
}

Utilisation (partie de test.c fourni par Insane Coding):

#include <stdio.h>
#include <stdlib.h>
#include "asprintf.h"

int main()
{
  char *s;
  if (asprintf(&s, "Hello, %d in hex padded to 8 digits is: %08x\n", 15, 15) != -1)
  {
    puts(s);
    insane_free(s);
  }
}
1
andertavares