2010-10-20 15 views
9

Estoy escribiendo un programa C que se espera que sea compilado con todos los compiladores. Actualmente estoy desarrollando en GCC en una máquina Linux y compilaré en MSVC antes de comprometer el código. Para facilitar la compilación cruzada, estoy compilando con los indicadores -ansi y -pedantic. Esto funcionó bien hasta que comencé a usar snprintf que no está disponible en el estándar C89. GCC puede compilar esto sin el modificador -ansi pero MSVC fallará siempre ya que no tiene soporte C99.snprintf en una aplicación multiplataforma

por lo que hice algo así,

#ifdef WIN32 
#define snprintf sprintf_s 
#endif 

Esto funciona bien porque snprintf y tiene sprintf_s mismas firmas. Me pregunto si este es el enfoque correcto.

+1

no es el estándar 'snprintf' para todas las C en cualquier plataforma? –

+2

no. 'snprintf' es parte del estándar C99. MSVC no tiene una implementación C99. –

+3

'sprintf_s' no es equivalente. 'snprintf' devuelve el número de caracteres que se han escrito, mientras' sprintf_s' devuelve -1 en el truncamiento. Ver [este debate] (http://social.msdn.microsoft.com/forums/en-US/vcgeneral/thread/2b339bdf-7ab1-4a08-bf7e-e9293801455b/). –

Respuesta

-5

Su enfoque está condenado al fracaso.

sqrt y cos tienen el mismo prototipo. ¿Crees que puedes intercambiarlos en un programa y obtener el mismo comportamiento antes/después del cambio?


Probablemente debe escribir su propia snprintf, o descargar una aplicación de Internet (google is your friend) y el uso que tanto en Linux y Windows.

+0

gracias. intentaré una implementación personalizada. –

+7

Esta respuesta no es útil. La pregunta planteada de otra manera: como snprintf y sprintf_s tienen la misma firma, ¿tienen también la misma funcionalidad? Su respuesta básicamente dice: No lo sé. –

14

Encontré this en el uso de _snprintf() como alternativa, y las complicaciones involucradas si la protección de desbordamiento del búfer realmente se dispara. Por lo que pude ver a simple vista, se aplican advertencias similares al sprintf_s.

¿Se puede ver el problema? En la versión de Linux, la salida siempre termina en nulo. En MSVC, no lo es.

Aún más sutil es la diferencia entre el parámetro size en Linux y el parámetro count en MSVC. El primero es el tamaño del búfer de salida que incluye el nulo de terminación y el último es el recuento máximo de caracteres para almacenar, que excluye el nulo de terminación.

Ah, y no olvides enviar un correo a Microsoft exigiendo que sean compatibles con los estándares de idiomas actuales. (Sé que ya anunciaron que no tienen ningún plan para admitir C99, pero que les molesta de todos modos. Se lo merecen.)

En pocas palabras, si quieres jugar de forma muy segura, deberás proporcionar tu propio snprintf() (un contenedor alrededor de _snprintf() o sprintf_s() detectando su comportamiento no estándar) para MSVC.

+0

Sugiero un "envoltorio de contrato" alrededor de las implementaciones existentes (como se menciona en la respuesta) en lugar de arrastrar cualquier código de terceros a su proyecto (como lo sugiere pmg). Una implementación completa '* printf()' es bastante grande. – DevSolar

+0

En realidad, una implementación completa de 'printf' puede ser muy pequeña. Normalmente estoy de acuerdo con tu principio de envolver implementaciones rotas en lugar de volver a implementarlas, pero dado que Windows '' * printf' también tiene algunas cosas rotas que no puedes eliminar fácilmente (como la interpretación hacia atrás de '% s' y'% ls' en las amplias variantes), me pregunto si el mejor enfoque podría ser reemplazarlo. –

+0

En realidad, otra cosa que puede solucionar al mismo tiempo es la inexacta impresión en punto flotante de MS. –

0

la respuesta más completa (se puede mejorar si se desea), puesto que en una etiqueta

#if __PLATFORM_WIN_ZERO_STANDARD__ 

    static inline 
    int LIBSYS_SNPRINTF(char * str, size_t size, const char * format, ...) 
    { 
     int retval; 
     va_list ap; 
     va_start(ap, format); 
     retval = _vsnprintf(str, size, format, ap); 
     va_end(ap); 
     return retval; 
    } 

    static inline 
    int LIBSYS_VASPRINTF(char **ret, char * format, va_list ap) 
    { 
     int wanted = vsnprintf(*ret = NULL, 0, format, ap); 
     if((wanted > 0) && ((*ret = LIBSYS_MALLOC(1 + wanted)) != NULL)) { 
      return vsprintf(*ret, format, ap); 
     } 
     return wanted; 
    } 

    static inline 
    int LIBSYS_ASPRINTF(char **ret, char * format, ...) 
    { 
     int retval; 
     va_list ap; 
     va_start(ap, format); 
     retval = LIBSYS_VASPRINTF(ret, format, ap); 
     va_end(ap); 
     return retval; 
    } 

#else 
    #define LIBSYS_SNPRINTF snprintf 
    #define LIBSYS_VASPRINTF vasprintf 
    #define LIBSYS_ASPRINTF asprintf 
#endif 
+0

Bastante seguro de que las funciones 'inline' y variadic son características C99. – Thomas

+3

@Thomas 'printf()' es una función variadica. Ha existido por un tiempo ... Entonces no, esa no es una adición de C99. – unwind

9

Su propuesta puede funcionar si usted está siendo cuidado.El problema es que tanto la función se comporta ligeramente diferente, si eso no es un problema para usted, usted es bueno ir, de lo contrario pensar en una función de contenedor:

Las diferencias entre MSVCs _snprintf y oficial C99 (gcc, sonido metálico) snprintf:

valor

de devolución:

  • MSVC: return -1 si el tamaño de búfer no lo suficiente como para escribir todo
  • GCC (sin incluir la terminación nula!): devuelve el número de caracteres que se hubiera escrito si buffer de gran tamaño suficiente

bytes escritos:

  • MSVC: escribir tanto como sea posible, no escriba NULL al final si no queda espacio
  • GCC: escribir tanto como sea posible, siempre escribir terminación NULL (excepción : buffer_size = 0)

interesante %n sutileza: Si utiliza %n en su código, MSVC dejará que no inicializado! si deja de analizar porque el tamaño del búfer es pequeño, GCC siempre escribirá el número de bytes que se habría escrito si el búfer hubiera sido lo suficientemente grande.

Así que mi propuesta sería escribir una función propia mysnprintf usando vsnprintf/_vsnprintf que da mismos valores de retorno y escribe los mismos bytes en ambas plataformas (tenga cuidado: %n es más difícil de corregir).

+0

Es un poco más fácil de envolver en el siguiente nivel. Tuve que hacer esto hace un tiempo, y el enfoque que utilicé tenía una función interna que asignaría() un búfer de un tamaño específico y llamaría vsnprintf; si el resultado encajaba, se desechaba en otro almacenamiento y la función retornaba OK. De lo contrario, la función devolvería un código 'intentar de nuevo con un tamaño de búfer mayor especificado'. Para gcc, se conocía el tamaño 'try-again' del retorno vsnprintf, y para MSVC simplemente seguiría aumentando en función de algunas heurísticas. – greggo

1

Puede abrir el archivo especial NUL para MSVC y escribir a eso. Siempre le indicará cuántos bytes se necesitan y no escribirá nada. De esta manera:

int main (int argc, char* argv[]) { 
    FILE* outfile = fopen("nul", "wb"); 
    int written; 

    if(outfile == NULL) { 
    fputs ("could not open 'nul'", stderr); 
    } 
    else { 
    written = fprintf(outfile, "redirect to /dev/null"); 
    fclose(outfile); 
    fprintf(stdout, "didn't write %d characters", written); 
    } 

    return 0; 
} 

Entonces debe saber cuántos bytes asignar para usar sprintf con éxito.

Cuestiones relacionadas