Mam funkcję, która akceptuje ciąg, czyli:
void log_out(char *);
Nazywając to, muszę w locie utworzyć sformatowany ciąg, taki jak:
int i = 1;
log_out("some text %d", i);
Jak to zrobić w ANSI C?
Tyle że skoro sprintf()
zwraca int, to znaczy, że muszę napisać co najmniej 3 komendy jak:
char *s;
sprintf(s, "%d\t%d", ix, iy);
log_out(s);
Jakiś sposób, aby to skrócić?
Odpowiedzi:
Użyj sprintf .
Parametry:
Przykład:
źródło
Jeśli masz system zgodny z POSIX-2008 (dowolny nowoczesny Linux), możesz skorzystać z bezpiecznej i wygodnej
asprintf()
funkcji:malloc()
wystarczy Ci pamięci, nie musisz martwić się o maksymalny rozmiar łańcucha. Użyj tego w ten sposób:Jest to minimalny wysiłek, jaki możesz wykonać, aby zbudować strunę w bezpieczny sposób.
sprintf()
Kod dałeś w pytaniu jest głęboko błędna:Za wskaźnikiem nie ma przydzielonej pamięci. Piszesz ciąg w losowym miejscu w pamięci!
Nawet jeśli napisałeś
miałbyś poważne kłopoty, ponieważ nie możesz wiedzieć, jaką liczbę umieścić w nawiasach.
Nawet gdybyś użył „bezpiecznego” wariantu
snprintf()
, nadal narażasz się na obcięcie sznurków. Podczas zapisywania do pliku dziennika jest to stosunkowo niewielki problem, ale może on potencjalnie odciąć precyzyjnie informacje, które byłyby przydatne. Ponadto odetnie końcowy znak końca linii, przyklejając następny wiersz dziennika do końca nieudanej linii.Jeśli spróbujesz użyć kombinacji
malloc()
isnprintf()
wytworzyć poprawne zachowanie we wszystkich przypadkach, otrzymasz około dwa razy więcej kodu niż podałemasprintf()
, i zasadniczo przeprogramujesz funkcjonalnośćasprintf()
.Jeśli zastanawiasz się nad dostarczeniem opakowania,
log_out()
które może przyjmowaćprintf()
samą listę parametrów stylu, możesz użyć wariantu,vasprintf()
który przyjmujeva_list
jako argument. Oto całkowicie bezpieczna implementacja takiego opakowania:źródło
asprintf()
nie jest to ani część standardowego C 2011, ani POSIX, ani nawet POSIX 2008 czy 2013. Jest to część TR 27431-2: zobacz Czy używasz "bezpiecznych" funkcji TR 24731?Wydaje mi się, że chcesz mieć możliwość łatwego przekazania ciągu utworzonego przy użyciu formatowania w stylu printf do funkcji, którą już masz, pobierającej prosty ciąg. Możesz utworzyć funkcję opakowującą, korzystając z
stdarg.h
udogodnień ivsnprintf()
(które mogą nie być łatwo dostępne, w zależności od kompilatora / platformy):W przypadku platform, które nie zapewniają dobrej implementacji (lub jakiejkolwiek implementacji)
snprintf()
rodziny procedur, z powodzeniem wykorzystałem prawie publiczną domenęsnprintf()
Holgera Weissa .źródło
Jeśli masz kod
log_out()
, przepisz go. Najprawdopodobniej możesz:Jeśli potrzebne są dodatkowe informacje logowania, można je wydrukować przed lub po wyświetlonym komunikacie. Oszczędza to alokację pamięci i wątpliwe rozmiary buforów i tak dalej i tak dalej. Prawdopodobnie musisz zainicjować
logfp
do zera (wskaźnik zerowy) i sprawdzić, czy jest pusty, i odpowiednio otworzyć plik dziennika - ale kod w istniejącym i taklog_out()
powinien sobie z tym radzić.Zaletą tego rozwiązania jest to, że można je po prostu nazwać tak, jakby to był wariant
printf()
; w rzeczywistości jest to niewielki wariantprintf()
.Jeśli nie masz kodu
log_out()
, zastanów się, czy możesz go zastąpić wariantem, takim jak opisany powyżej. To, czy możesz użyć tej samej nazwy, będzie zależeć od struktury aplikacji i ostatecznego źródła bieżącejlog_out()
funkcji. Jeśli znajduje się w tym samym pliku obiektowym, co inna niezbędna funkcja, musiałbyś użyć nowej nazwy. Jeśli nie możesz dowiedzieć się, jak dokładnie to powtórzyć, będziesz musiał użyć jakiegoś wariantu, takiego jak podane w innych odpowiedziach, który przydziela odpowiednią ilość pamięci.Oczywiście teraz wywołujesz
log_out_wrapper()
zamiastlog_out()
- ale alokacja pamięci i tak dalej jest wykonywana raz. Zastrzegam sobie prawo do nadmiernego przydzielania miejsca o jeden niepotrzebny bajt - nie sprawdziłem dwukrotnie, czy długość zwracana przezvsnprintf()
zawiera kończący null, czy nie.źródło
Nie używaj sprintf.
Spowoduje to przepełnienie bufora ciągów i awarię programu.
Zawsze należy używać sprintf
źródło
Nie zrobiłem tego, więc po prostu wskażę właściwą odpowiedź.
C ma przepisy dla funkcji, które pobierają nieokreśloną liczbę operandów, używając
<stdarg.h>
nagłówka. Możesz zdefiniować swoją funkcję jakovoid log_out(const char *fmt, ...);
i uzyskać jejva_list
wnętrze. Następnie możesz przydzielić pamięć i dzwonićvsprintf()
z przydzieloną pamięcią, formatem iva_list
.Alternatywnie, możesz użyć tego do napisania funkcji analogicznej do
sprintf()
tej, która przydzieliłaby pamięć i zwróciła sformatowany ciąg, generując go mniej więcej tak, jak powyżej. Byłby to wyciek pamięci, ale jeśli się wylogujesz, może to nie mieć znaczenia.źródło
http://www.gnu.org/software/hello/manual/libc/Variable-Arguments-Output.html podaje następujący przykład drukowania na stderr. Możesz go zmodyfikować, aby zamiast tego używał funkcji dziennika:
Zamiast vfprintf będziesz musiał użyć vsprintf, gdzie musisz zapewnić odpowiedni bufor do drukowania.
źródło