2010-05-26 26 views
94

soy la mala suerte de ser atrapado utilizando VS 2010 para un proyecto, y se fijó el siguiente código aún no construye usando el compilador no compatible con las normas:snprintf y Visual Studio 2010

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

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

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

    return 0; 
} 

(falla compilación con el error: C3861: 'snprintf': identificador no encontrado)

Recuerdo que este es el caso de volver atrás con VS 2005 y estoy sorprendido de ver que todavía no se ha solucionado.

¿Alguien sabe si Microsoft tiene algún plan para mover sus bibliotecas estándar de C al año 2010?

+1

... o simplemente puede hacer "_snprintf #define snprintf" –

+4

... se puede, pero por desgracia _snprintf() no es lo mismo que snprintf() como no garantiza la terminación nula. –

+0

Ok, entonces necesitarás ponerlo en cero antes de usar _snprintf(). También estoy de acuerdo contigo. Desarrollar bajo MSVC es horrible. Los errores también son confusos. – Owl

Respuesta

3

creo que el equivalente de Windows es sprintf_s

+7

'sprintf_s' se comporta de forma diferente a' snprintf'. – interjay

+0

Específicamente sprintf_s docs say, "Si el búfer es demasiado pequeño para el texto que se está imprimiendo, entonces el búfer se establece en una cadena vacía". En contraste, snprintf escribe una cadena truncada en la salida. –

+2

@AndrewBainbridge: truncó la documentación. La oración completa es "Si el búfer es demasiado pequeño para el texto que se imprime, entonces el búfer se establece en una cadena vacía y se invoca el controlador de parámetros no válido". El comportamiento predeterminado para el identificador de parámetro no válido es finalizar su programa. Si quiere truncar con la familia _s, entonces necesita usar snprintf_s y el indicador _TRUNCATE. Sí, es desafortunado que las funciones _s no proporcionen una forma conveniente de truncar. Por otro lado, las funciones _s usan magia de plantilla para inferir tamaños de búfer, y eso es excelente. –

32

snprintf no es parte de C89. Es estándar solo en C99. Microsoft tiene no plan supporting C99.

(pero también es estándar en C++ 0x ...!)

Ver otras respuestas a continuación para una solución.

+5

No es una buena solución, sin embargo ... ya que hay diferencias en el comportamiento snprintf y _snprintf. _snprintf maneja el terminador nulo de forma retardada cuando se trata de espacio de búfer insuficiente. – Andrew

+0

Aquí está su cita - http://connect.microsoft.com/VisualStudio/feedback/details/333273/request-for-c99-vla-in-visual-studio –

+1

@jsumners: Gracias :) @ Andrew: Es por eso que he inventado 'sprintf_s'. :( – kennytm

77

Relato breve: Microsoft finalmente ha implementado snprintf en Visual Studio 2015. En versiones anteriores, puede simularlo de la siguiente manera.


Versión larga:

Aquí es el comportamiento esperado para snprintf:

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

Writes at most buf_size - 1 characters to a buffer. The resulting character string will be terminated with a null character, unless buf_size is zero. If buf_size is zero, nothing is written and buffer may be a null pointer. The return value is the number of characters that would have been written assuming unlimited buf_size , not counting the terminating null character.

Las versiones anteriores a Visual Studio 2015 no tenía una aplicación conforme. En su lugar, hay extensiones no estándar como _snprintf() (que no escribe null-terminator en desbordamiento) y _snprintf_s() (que puede imponer la terminación nula, pero devuelve -1 en desbordamiento en lugar de la cantidad de caracteres que se habrían escrito) .

repliegue sugerido para VS 2005 y hasta:

#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 
+0

Esto no siempre terminará la cadena con un 0 que se requiere en un desbordamiento. El segundo si en c99_vsnprintf debe ser: if (count == -1) { if (size> 0) str [size-1] = 0; count = _vscprintf (format, ap); } – Lothar

+1

@Lothar: El búfer siempre tiene terminación nula.De acuerdo con MSDN: "si el truncamiento de cadena está habilitado al pasar _TRUNCATE, estas funciones copiarán solo la cantidad de cadena que corresponda, dejando el búfer de destino anulado y regresará con éxito". –

+0

¿no deberías intercambiar el orden de estas dos funciones para que las primeras llamadas sean segundos, entonces el segundo debe ser el primero y el primero debe ser el segundo? – javapowered

2

Otra sustitución segura de snprintf() y vsnprintf() es proporcionada por ffmpeg. Puede consultar el source here (sugerido).

8

Si usted no necesita el valor de retorno, se podría también acaba de definir como snprintf _snprintf_s

#define snprintf(buf,len, format,...) _snprintf_s(buf, len,len, format, __VA_ARGS__) 
0

Probé código de @Valentin Milea pero tengo errores de infracción de acceso. Lo único que funcionó para mí fue la implementación de Insane Coding: http://asprintf.insanecoding.org/

Específicamente, estaba trabajando con el código heredado de VC++ 2008. De la implementación de Insane Coding (se puede descargar desde el enlace de arriba), utilicé tres archivos: asprintf.c, asprintf.h y vasprintf-msvc.c. Otros archivos fueron para otras versiones de MSVC.

[EDIT] Para completar, sus contenidos son los siguientes:

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); 
} 

Uso (parte o f test.c ha proporcionado mediante la codificación Loco):

#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); 
    } 
} 
+0

Buen punto, editado, responda más autónomo. – andertavares