Czy snprintf zawsze przerywa bufor docelowy?
Innymi słowy, czy to wystarczy:
char dst[10];
snprintf(dst, sizeof (dst), "blah %s", somestr);
czy też musisz to robić, jeśli jakiś czas jest wystarczająco długi?
char dst[10];
somestr[sizeof (dst) - 1] = '\0';
snprintf(dst, sizeof (dst) - 1, "blah %s", somestr);
Interesuje mnie zarówno to, co mówi standard, jak i co może zrobić niektóre popularne libc, co nie jest standardowym zachowaniem.
Odpowiedzi:
Jak ustalają inne odpowiedzi: Powinien :
Więc wszystko, co musisz uważać, to aby nie przekazać do niego bufora o rozmiarze zerowym, ponieważ (oczywiście) nie może on zapisać zera „nigdzie”.
Jednak uważaj , że biblioteki Microsoftu
nie posiada funkcję o nazwiehistorycznie tylko miał funkcję o nazwiesnprintf
lecz_snprintf
(uwaga wiodącym podkreślenia), które nie dołączania null obciążeniowy. Oto dokumenty (VS 2012, ~~ VS 2013):http://msdn.microsoft.com/en-us/library/2ts7cx93%28v=vs.110%29.aspx
Visual Studio 2015 (VC14) najwyraźniej wprowadził zgodną
snprintf
funkcji, ale jeden z wiodących Legacy podkreślenia i non zerowej kończącego zachowań nadal istnieje:źródło
_snprintf
który po cichu usuwa kluczową funkcję bezpieczeństwasnprintf
i pozwala, aby ciąg nie był zakończony zerem ?!template <size_t size> int _snprintf_s(char (&buffer)[size], size_t count, const char *format [, argument] ...);
i powinienem też wspomnieć, że dzieje się to tylko z flagą kompilacji / GS (Security Check). Ta funkcja zna rozmiar, liczbę i długość.Zgodnie ze stroną podręcznika snprintf (3).
Więc tak, nie trzeba przerywać, jeśli rozmiar> = 1.
źródło
Zgodnie ze standardem C, chyba że rozmiar bufora wynosi 0
vsnprintf()
isnprintf()
null kończy jego wyjście.Tak więc, jeśli chcesz wiedzieć, jak duży bufor ma zostać przydzielony, użyj rozmiaru zerowego, a następnie możesz użyć wskaźnika zerowego jako miejsca docelowego. Zauważ, że linkowałem do stron POSIX, ale te wyraźnie mówią, że nie ma żadnej rozbieżności między Standardem C a POSIX, jeśli dotyczą tego samego zagadnienia:
Uważaj na wersję Microsoft
vsnprintf()
. Zdecydowanie zachowuje się inaczej niż standardowa wersja C, gdy nie ma wystarczającej ilości miejsca w buforze (zwraca -1, gdzie standardowa funkcja zwraca wymaganą długość). Nie jest do końca jasne, że wersja Microsoft null kończy swoje wyjście w przypadku wystąpienia błędu, podczas gdy standardowa wersja C.Zwróć także uwagę na odpowiedzi na pytanie Czy używasz bezpiecznych funkcji TR 24731? (zobacz MSDN dla wersji Microsoft
vsprintf_s()
) i rozwiązanie Mac dla bezpiecznych alternatyw dla niebezpiecznych funkcji biblioteki C?źródło
Niektóre starsze wersje SunOS robiły dziwne rzeczy z snprintf i mogły nie przerywać wyjścia NUL i zwracać wartości, które nie pasowały do tego, co robili wszyscy inni, ale wszystko, co zostało wydane w ciągu ostatnich 10 lat, robiło to, co C99 mówi.
źródło
Niejednoznaczność zaczyna się od samego standardu C. Zarówno C99, jak i C11 mają identyczny opis
snprintf
funkcji. Oto opis z C99:Z jednej strony zdanie
mówi, że
jeśli (
s
wskazuje na tablicę składającą się z 3 znaków i)n
wynosi 3, to zostaną zapisane 2 znaki, a znaki poza drugim są odrzucane ; wtedy znak null jest zapisywany po tych 2 (a znak null będzie trzecim zapisanym znakiem) .I to, jak sądzę, odpowiada na pierwotne pytanie.
ODPOWIEDŹ:
Jeśli kopiowanie odbywa się między nakładającymi się obiektami, zachowanie jest niezdefiniowane.
Jeśli
n
wynosi 0, nic nie jest zapisywane na wyjściu, wprzeciwnym razie, jeśli nie napotkano błędów kodowania, wyjście jest ZAWSZE zakończone znakiem null ( niezależnie od tego, czy dane wyjściowe mieszczą się w tablicy wyjściowej, czy nie ; jeśli nie, to niektóre znaki są odrzucane, tak że wynik tablica nigdy nie jest przepełniona), w
przeciwnym razie (jeśli wystąpią błędy kodowania) dane wyjściowe mogą pozostać niezerowe .
Z drugiej strony
Ostatnie zdanie
daje niejednoznaczność (lub mój angielski nie jest wystarczająco dobry). Mogę zinterpretować to zdanie na co najmniej dwa sposoby:
1. Wyjście jest zakończone wartością zerową wtedy i tylko wtedy, gdy zwrócona wartość jest nieujemna i mniejsza niż
n
(co oznacza, że jeśli zwrócona wartość jest nie mniejsza niżn
, tj. Dane wyjściowe (w tym kończący znak null) nie mieści się w tablicy, wtedy dane wyjściowe nie są zakończone znakiem null ).2. Dane wyjściowe są kompletne (żadne znaki nie zostały odrzucone) wtedy i tylko wtedy, gdy zwrócona wartość jest nieujemna i mniejsza niż
n
.Uważam, że powyższa interpretacja 1 jest sprzeczna z ODPOWIEDZI, powoduje nieporozumienia i długie dyskusje. Dlatego ostatnie zdanie opisujące
snprintf
funkcję wymaga zmiany w celu usunięcia wszelkich niejasności (co daje podstawy do napisania Propozycji na Standard języka C).Uważam, że przykład niejednoznacznego sformułowania można znaleźć na stronie http://en.cppreference.com/w/c/io/fprintf (zobacz
4)
), dzięki @ "Martin Ba" za link.Zobacz także pytanie „ snprintf: Czy są jakieś propozycje / plany C Standardu zmiany opisu tej funkcji? ”.
źródło