Unix a Windows: ¿alternativa a vsnprintf para determinar la longitud?

Actualmente estoy convirtiendo el código de una de nuestras bibliotecas de Linux a una DLL de Windows.

Dentro de esta biblioteca tengo una función que toma los últimos parámetros en una forma printf (cadena de formato, luego puntos suspensivos). Dentro de esta función, uso vsnprintf para formatear los argumentos proporcionados. Como quiero saber si puedo meter la cadena final en un búfer pequeño o si tendría que asignarle memoria, estoy interesado en determinar la "longitud posible" de la cadena formateada.

Para hacer esto, actualmente estoy usando vsnprintf como este (código de ejemplo compuesto, obviamente):

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

Este uso está cubierto por la Especificaciones de base de grupo abierto Edición 6:

vsnprintf (char * restringir s, tamaño_t n, const char * restringir formato, va_list ap)

Las funciones [...] vsnprintf () [...] serán equivalentes a snprintf ().

snprintf (char * restringir s, tamaño_t n, const char * restringir formato, ...)

Si el valor de n es cero en una llamada a snprintf (), no se escribirá nada, se devolverá el número de bytes que se habrían escrito si n hubiera sido lo suficientemente grande excluyendo el nulo de terminación, y s puede ser un puntero nulo.

El problema surgió cuando estaba compilando este código en el sistema Windows (Visual Studio 2010) con / analizar en. El compilador / analizador me dio lo siguiente:

test.c (11): advertencia C6309: El argumento '1' es nulo: esto no cumple con la especificación de función de 'vsnprintf'

test.c (11): advertencia C6387: 'argumento 1' podría ser '0': esto no cumple con la especificación de la función 'vsnprintf': Líneas: 7, 8, 10, 11

Un vistazo rápido al Entrada de MSDN para vsnprintf me dio esto:

If buffer or formato is NULL, o si el recuento es menor o igual a cero, estas funciones invocan el controlador de parámetros no válido, como se describe en Validación de parámetros. Si se permite que la ejecución continúe, estas funciones devuelven -1.

Curiosamente, el ejemplo anterior funciona, no obstante, en Windows "como se esperaba" (es decir, me devuelve el recuento de caracteres que se escribirían).

Pero como no quiero que esto dependa de algo no especificado, me gustaría saber si hay una forma oficial mejor de lograr lo mismo, sin tener que esperar que esto no se rompa en alguna versión futura.

¡gracias por tu tiempo!

preguntado el 13 de diciembre de 11 a las 07:12

Habilite C99 en su compilador, creo que debe estar disponible. O utiliza ijs.si/software/snprintf -

@Geo No en los compiladores de Microsoft. -

Esto parece más un problema de documentación que cualquier otra cosa. Las fuentes de debug crt en vs9 permiten explícitamente esta situación: _VALIDATE_RETURN( (count == 0) || (string != NULL), EINVAL, -1 ); y por debajo if ( string==NULL) return(retval);, entonces pensaron en el caso donde count == 0 y string == NULL. -

@Artefacto: Gracias por buscar eso. Escribiré un correo a la gente de documentación de MSDN. Quizás puedan aclarar su redacción. -

Considere usar _vscprintf () en su lugar. -

2 Respuestas

Respuesta proporcionada en comentarios por Hans Passant:

El documentado _vscprintf proporciona esta funcionalidad en Windows, por lo que no es necesario confiar en un "comportamiento no especificado".

Respondido 29 Oct 20, 18:10

En realidad, no es un "comportamiento no especificado", es un comportamiento contrario a la especificación. - artefacto

El MSDN _vscprintf El enlace del documento en esta respuesta está desactualizado. Aquí hay un enlace actualizado para 2020: docs.microsoft.com/en-us/cpp/c-runtime-library/reference/… - jeff rueda

Tome este ejemplo del página de manual de snprintf:

A continuación, se explica cómo asignar un búfer para que se ajuste a su cadena.

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

Respondido el 13 de diciembre de 11 a las 13:12

Conozco esta posibilidad. Sin embargo, quiero evitar los mallocs cuando sea necesario, por lo que este no es un reemplazo exacto de lo que estaba buscando. Sin embargo, gracias por buscar ese ejemplo :) - lx.

La mejor respuesta para este (v) snprintf en Visual Studio y su diferencia con posix se responde aquí con un buen código de muestra: stackoverflow.com/questions/2915672/… - Vicente Ricosti

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.