Tengo un código que convierte parámetros variados en un va_list
, luego pasa la lista a una función que luego llama a vsnprintf
. Esto funciona bien en Windows y OS X, pero está fallando con resultados extraños en Linux.va_list mal comportamiento en Linux
En el siguiente ejemplo de código:
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
char *myPrintfInner(const char *message, va_list params)
{
va_list *original = ¶ms;
size_t length = vsnprintf(NULL, 0, message, *original);
char *final = (char *) malloc((length + 1) * sizeof(char));
int result = vsnprintf(final, length + 1, message, params);
printf("vsnprintf result: %d\r\n", result);
printf("%s\r\n", final);
return final;
}
char *myPrintf(const char *message, ...)
{
va_list va_args;
va_start(va_args, message);
size_t length = vsnprintf(NULL, 0, message, va_args);
char *final = (char *) malloc((length + 1) * sizeof(char));
int result = vsnprintf(final, length + 1, message, va_args);
printf("vsnprintf result: %d\r\n", result);
printf("%s\r\n", final);
va_end(va_args);
return final;
}
int main(int argc, char **argv)
{
char *test = myPrintf("This is a %s.", "test");
char *actual = "This is a test.";
int result = strcmp(test, actual);
if (result != 0)
{
printf("%d: Test failure!\r\n", result);
}
else
{
printf("Test succeeded.\r\n");
}
return 0;
}
La salida del segundo vsnprintf
llamada es 17, y el resultado de strcmp
es 31; pero yo no entiendo por qué vsnprintf
regresarían 17 This is a test.
ya que tiene 15 caracteres, añadir el NULL
y se obtiene 16.
hilos relacionados que he visto, pero no abordan el tema:
con la respuesta de @ Mat (estoy reutilizando el va_list
objeto, que no está permitido), esto viene directamente al primer hilo relacionado al que me he vinculado. Así que he tratado este código en su lugar:
char *myPrintfInner(const char *message, va_list params)
{
va_list *original = ¶ms;
size_t length = vsnprintf(NULL, 0, message, params);
char *final = (char *) malloc((length + 1) * sizeof(char));
int result = vsnprintf(final, length + 1, message, *original);
printf("vsnprintf result: %d\r\n", result);
printf("%s\r\n", final);
return final;
}
cual, per the C99 spec (nota al pie en la Sección 7.15), debería funcionar:
Está permitido crear un puntero a una va_list y pasar ese puntero a otro función, en cuyo caso la función original puede hacer uso posterior de la lista original después de que la otra función regrese.
Pero mi compilador (gcc 4.4.5 en modo C99) me da este error con respecto a la primera línea de myPrintfInner
:
test.c: In function ‘myPrintfInner’:
test.c:8: warning: initialization from incompatible pointer type
Y el binario resultante produce el mismo efecto que en la primera ronda .
encontrado esto: Is GCC mishandling a pointer to a va_list passed to a function?
La solución sugerida (que no se garantiza que funcione, pero sí en la práctica) es el uso de arg_copy
primera:
char *myPrintfInner(const char *message, va_list params)
{
va_list args_copy;
va_copy(args_copy, params);
size_t length = vsnprintf(NULL, 0, message, params);
char *final = (char *) malloc((length + 1) * sizeof(char));
int result = vsnprintf(final, length + 1, message, args_copy);
printf("vsnprintf result: %d\r\n", result);
printf("%s\r\n", final);
return final;
}
Su función 'myPrintf' le falta una declaración' return'. Esperaba que tu compilador te advirtiera sobre eso. –
bah, humbug! Copiar y pegar el error. –
Su nuevo código está haciendo exactamente lo mismo que el anterior: 'original' apunta a' params', por lo que pasar '* original' es exactamente lo mismo que pasar' params'. Su verdadero problema parece ser que no entiende cómo funciona 'va_list': son esencialmente punteros a la pila de argumentos, y el puntero se incrementa a medida que se usa. Entonces, cuando usa el mismo 'va_list' dos veces, la segunda vez está incrementando el puntero más allá del final de la lista de argumentos. –