Ho un codice che converte i parametri variadici in un va_list
, quindi passa l'elenco su una funzione che chiama quindi vsnprintf
. Funziona bene su Windows e OS X, ma non funziona con risultati strani su Linux.comportamento scorretto va_list su Linux
Nel seguente esempio di codice:
#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;
}
L'uscita della seconda vsnprintf
chiamata è 17, e il risultato di strcmp
è 31; ma io non capisco perché vsnprintf
sarebbero tornati 17 visto che This is a test.
è di 15 caratteri, aggiungere il NULL
e si ottiene 16.
discussioni correlate che ho visto, ma non affrontano il tema:
Con risposta @ di Mat (sto riutilizzare laOggetto, che non è consentito), questo viene esattamente intorno al primo thread correlato a cui mi sono collegato. Così ho tentato questo codice invece:
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;
}
Che, per the C99 spec (nota in calce alla Sezione 7.15), dovrebbe funzionare:
È consentito di creare un puntatore ad una va_list e superare tale puntatore ad un altro funzione, nel qual caso la funzione originale potrebbe rendere ulteriore uso della lista originale dopo che l'altra funzione è tornata.
Ma il mio compilatore (gcc 4.4.5 in modalità C99) mi dà questo errore per quanto riguarda la prima linea di myPrintfInner
:
test.c: In function ‘myPrintfInner’:
test.c:8: warning: initialization from incompatible pointer type
E il binario risultante produce lo stesso effetto esattamente come la prima volta intorno .
trovato questo: Is GCC mishandling a pointer to a va_list passed to a function?
La soluzione suggerita (che non è stato garantito il funzionamento, ma ha fatto in pratica) è quello di utilizzare arg_copy
prima:
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;
}
La funzione 'myPrintf' manca un'istruzione' return'. Mi sarei aspettato che il tuo compilatore ti avvisasse di questo. –
bah, humbug! Copia e incolla errore. –
Il tuo nuovo codice fa esattamente la stessa cosa del vecchio: 'original' punta a' params', quindi passare '* original' è esattamente come passare' params'. Il tuo vero problema sembra essere che non capisci come funzionano 'va_list: sono essenzialmente dei puntatori allo stack degli argomenti e il puntatore viene incrementato man mano che viene usato. Quindi, quando si utilizza lo stesso 'va_list' due volte, la seconda volta si incrementa il puntatore oltre la fine dell'elenco degli argomenti. –