2010-05-26 23 views
94

Sono la sfortuna di essere bloccato utilizzando VS 2010 per un progetto, e ho notato il seguente codice ancora non costruisce utilizzando il non-standard compilatore compatibile:snprintf e Visual Studio 2010

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

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

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

    return 0; 
} 

(fallisce compilazione con errore: C3861: 'snprintf': identificatore non trovato)

Mi ricordo che questo è il caso di ritorno con VS 2005 e sono scioccato nel vedere che non è stato ancora risolto.

Qualcuno sa se Microsoft ha intenzione di spostare le librerie C standard nell'anno 2010?

+1

... oppure si può semplicemente fare "#define snprintf _snprintf" –

+4

... si potrebbe, ma purtroppo _snprintf() non è la stessa di snprintf() come non garantisce la risoluzione nulla. –

+0

Ok quindi dovrai memsetare a zero prima di usare _snprintf(). Inoltre sono d'accordo con te. Lo sviluppo sotto MSVC è terribile. Anche gli errori sono confusi. – Owl

risposta

3

credo che l'equivalente di Windows è sprintf_s

+7

'sprintf_s' si comporta diversamente da' snprintf'. – interjay

+0

In particolare i documenti sprintf_s dicono "Se il buffer è troppo piccolo per il testo che viene stampato, allora il buffer è impostato su una stringa vuota". Al contrario, snprintf scrive una stringa troncata sull'output. –

+2

@AndrewBainbridge - hai troncato la documentazione. La frase completa è "Se il buffer è troppo piccolo per il testo che viene stampato, il buffer viene impostato su una stringa vuota e viene invocato il gestore parametri non valido." Il comportamento predefinito per l'handle del parametro non valido è terminare il programma. Se si desidera il troncamento con la famiglia _s, è necessario utilizzare snprintf_s e il flag _TRUNCATE. Sì, è un peccato che le funzioni _s non diano un modo conveniente di troncare. D'altra parte, le funzioni _s usano la magia del modello per dedurre le dimensioni del buffer, e questo è eccellente. –

32

snprintf non fa parte della C89. È standard solo nel C99. Microsoft ha no plan supporting C99.

(ma è anche standard C++ 0x ...!)

Vedi altre risposte qui sotto per una soluzione alternativa.

+5

Non è una buona soluzione, tuttavia ... poiché esistono differenze nel comportamento di snprintf e _snprintf. _snprintf gestisce il terminatore null ritardato quando si occupa di spazio del buffer insufficiente. – Andrew

+0

Ecco la tua citazione - http://connect.microsoft.com/VisualStudio/feedback/details/333273/request-for-c99-vla-in-visual-studio –

+1

@jsumners: Grazie :) @Andrew: Ecco perché loro ho inventato 'sprintf_s'. :( – kennytm

77

Breve storia: Microsoft ha finalmente implementato snprintf in Visual Studio 2015. Nelle versioni precedenti è possibile simularlo come di seguito.


Versione lunga:

Ecco il comportamento previsto per 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.

release precedenti a Visual Studio 2015 non hanno avuto un'implementazione conforme. Esistono invece estensioni non standard come _snprintf() (che non scrive null-terminator su overflow) e _snprintf_s() (che può imporre la terminazione null, ma restituisce -1 in overflow anziché il numero di caratteri che sarebbero stati scritti) .

fallback consigliata per VS 2005 e fino:

#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

Questo non risolverà sempre la stringa con uno 0 che è richiesto su un overflow. Il secondo se in c99_vsnprintf deve essere: if (count == -1) { if (size> 0) str [size-1] = 0; count = _vscprintf (formato, ap); } – Lothar

+1

@Lothar: il buffer è sempre terminato da null.Secondo MSDN: "se il troncamento delle stringhe viene abilitato passando _TRUNCATE, queste funzioni copieranno solo la parte della stringa che si adatta, lasciando il buffer di destinazione terminato da null e restituito correttamente". –

+0

non dovresti scambiare l'ordine di queste due funzioni causa le prime chiamate secondi, quindi il secondo deve essere il primo e il primo deve essere il secondo? – javapowered

2

Un altro sostituto sicuro di snprintf() e vsnprintf() è fornito da ffmpeg. Puoi effettuare il checkout allo source here (suggerito).

8

Se non è necessario il valore di ritorno, si potrebbe anche solo definire come snprintf _snprintf_s

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

ho provato il codice di @Valentin Milea ma ho errori di violazione di accesso. L'unica cosa che ha funzionato per me è stata l'implementazione di Insane Coding: http://asprintf.insanecoding.org/

In particolare, stavo lavorando con il codice legacy VC++ 2008. Dall'implementazione di Insane Coding (scaricabile dal link sopra), ho utilizzato tre file: asprintf.c, asprintf.h e vasprintf-msvc.c. Altri file erano per altre versioni di MSVC.

[EDIT] Per completezza, il loro contenuto è il seguente:

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

Utilizzo (parte o f test.c fornita dalle Coding Insane):

#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

Buon punto, modificato, rendere la risposta più self-contained. – andertavares