Mam kod, który konwertuje parametry variadic na va_list
, a następnie przekazuje listę do funkcji, która następnie wywołuje vsnprintf
. Działa to dobrze w systemach Windows i OS X, ale nie działa z nieparzystymi wynikami w systemie Linux.va_list złe zachowanie w systemie Linux
W poniższym przykładzie KOD:
#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;
}
Wyjście drugiego vsnprintf
połączenia wynosi 17, a wynik strcmp
wynosi 31; ale ja nie rozumiem dlaczego vsnprintf
wróci 17 skoro This is a test.
jest 15 znaków, dodaj NULL
i masz 16.
Podobne wątki, które widziałam, ale nie rozwiązania tematu:
z odpowiedzi @ Mat za (jestem ponownym użyciem va_list
obiekt, który nie jest dozwolony), przychodzi prosto do pierwszego powiązanego wątku, z którym się łączyłem. Więc próbowałem tego kodu:
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;
}
, które per the C99 spec (przypis w punkcie 7.15), powinno działać:
Dozwolone jest tworzenie wskaźnik do va_list i przekazać ten wskaźnik do drugiego funkcja, w którym to przypadku oryginalna funkcja może spowodować dalsze użycie oryginalnej listy po zwróceniu drugiej funkcji.
Ale mój kompilator (gcc 4.4.5 w trybie C99) daje mi ten błąd dotyczący pierwszej linii myPrintfInner
:
test.c: In function ‘myPrintfInner’:
test.c:8: warning: initialization from incompatible pointer type
i otrzymaną binarny produkuje dokładnie ten sam efekt, jak po raz pierwszy w okolicy .
Znalazłem to: Is GCC mishandling a pointer to a va_list passed to a function?
Sugerowana obejście (co nie było gwarantowane do pracy, ale nie w praktyce) jest użycie arg_copy
pierwszy:
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;
}
W swojej funkcji 'myPrintf' brakuje instrukcji" return ". Spodziewałbym się, że twój kompilator ostrzeże cię o tym. –
bah, humbug! Skopiuj i wklej błąd. –
Twój nowy kod robi dokładnie to samo, co stary: 'original' wskazuje na' params', więc przekazywanie '* original' jest dokładnie takie samo jak przekazywanie' params'. Twoim prawdziwym problemem wydaje się być to, że nie rozumiesz, jak działa 'va_list': są one w zasadzie wskaźnikami do stosu argumentów, a wskaźnik jest zwiększany, gdy jest używany. Więc kiedy dwukrotnie używasz tej samej 'va_list', po raz drugi zwiększasz wskaźnik za końcem listy argumentów. –