Beendet snprintf() IMMER null?

Lesezeit: 10 Minuten

Benutzeravatar von Prof. Falken
Prof. Falken

Beendet snprintf immer null den Zielpuffer?

Mit anderen Worten, ist dies ausreichend:

char dst[10];

snprintf(dst, sizeof (dst), "blah %s", somestr);

oder muss man das so machen, wenn somestr lang genug ist?

char dst[10];

somestr[sizeof (dst) - 1] = '\0';
snprintf(dst, sizeof (dst) - 1, "blah %s", somestr);

Ich interessiere mich sowohl für das, was der Standard sagt, als auch für das, was einige populäre libc tun könnten, was kein Standardverhalten ist.

  • Wollen Sie im zweiten Beispiel somestr oder dst mit null beenden?

    – Hudson

    23. April 2013 um 18:34 Uhr

  • @chux, Martin Ba hat das in der akzeptierten Antwort behandelt. 🙂

    – Prof. Falken

    6. Oktober 2014 um 14:53 Uhr

  • @chux fand ich gut, dein kommentar hat gerade sehr deutlich gemacht, dass wenn dest i 0 lang ist, nichts geschrieben wird. Ich nehme jeden Kommentar als möglichen Vorwand, um mit anderen Stackoverflowers zu plaudern. 🙂

    – Prof. Falken

    6. Oktober 2014 um 16:03 Uhr

  • @Prof. Falken Stimmen Sie zu, dass dieser Kommentar in Ordnung und explizit war, aber er war mit den Antworten überflüssig – das habe ich in meiner Rezension gerade übersehen.

    – chux – Wiedereinsetzung von Monica

    6. Oktober 2014 um 16:21 Uhr

  • stackoverflow.com/a/8712996/193892 Visual Studio unterstützt jetzt snprintf()

    – Prof. Falken

    29. April 2020 um 13:19 Uhr


Benutzeravatar von Martin Ba
Martin BA

Wie die anderen Antworten belegen: Es sollte:

snprintf … schreibt die Ergebnisse in einen Zeichenkettenpuffer. (…) wird mit einem Nullzeichen abgeschlossen, es sei denn, buf_size ist null.

Sie müssen also nur darauf achten, dass Sie ihm keinen Puffer der Größe Null übergeben, da er (offensichtlich) keine Null in “nirgendwo” schreiben kann.


Jedoch, in acht nehmen dass Microsofts Bibliothek hat nicht eine aufgerufene Funktion snprintf aber stattdessen historisch nur hatte eine Funktion aufgerufen _snprintf (beachten Sie den führenden Unterstrich) welche nicht anhängen eine abschließende Null. Hier ist die Dokumentation (VS 2012, ~~ VS 2013):

http://msdn.microsoft.com/en-us/library/2ts7cx93%28v=vs.110%29.aspx

Rückgabewert

Lassen Sie len die Länge des formatierten Datenstrings sein (ohne die abschließende Null). len und count sind in Byte für _snprintf, breite Zeichen für _snwprintf.

  • Wenn len < count, werden len-Zeichen im Puffer gespeichert, ein Null-Terminator angehängt und len zurückgegeben.

  • Wenn len = count, dann werden len-Zeichen im Puffer gespeichert, es wird kein Nullterminator angehängt und len wird zurückgegeben.

  • Wenn len > count, dann werden count Zeichen im Puffer gespeichert, es wird kein Nullterminator angehängt und es wird ein negativer Wert zurückgegeben.

(…)

Visual Studio 2015 (VC14) hat anscheinend die Anpassung eingeführt snprintf Funktion, aber die Legacy-Funktion mit dem führenden Unterstrich und der nicht null-beendendes Verhalten ist immer noch da:

Das snprintf Die Funktion schneidet die Ausgabe ab, wenn len größer oder gleich count ist, indem ein Nullterminator bei gesetzt wird
buffer[count-1]. (…)

Für alle Funktionen Sonstiges als snprintfwenn len = count, werden len-Zeichen im Puffer gespeichert, es wird kein Nullterminator angehängt(…)

  • Was im Namen von Aslan haben sich die Microsoft-Ingenieure bei der Einführung gedacht _snprintf was leise entfernt ein wichtiges Sicherheitsmerkmal von snprintf und erlaubt, dass der String nicht nullterminiert wird?!

    – Colin D. Bennett

    6. Oktober 2014 um 15:50 Uhr


  • @ColinDBennett – es ist komisch und mächtig nervig und ich habe keine Ahnung, ob überhaupt jemand daran gedacht hat 🙂

    – Martin B

    6. Oktober 2014 um 18:07 Uhr

  • @MartinBa ja sorry, was ich getestet habe war template <size_t size> int _snprintf_s(char (&buffer)[size], size_t count, const char *format [, argument] ...); und ich sollte auch erwähnen, dass dies nur mit /GS (Security Check) Compiler-Flag geschieht. Diese Funktion kennt Größe, Anzahl und Länge.

    – sekmet64

    17. November 2014 um 14:53 Uhr

  • Beachten Sie, dass mingw64 die Implementierung von Microsoft _snprintf als “normales” snprintf verwendet (verwendet?), sofern nicht anders angegeben nvd.nist.gov/vuln/detail/CVE-2018-1000101

    – Domenukk

    19. Dezember 2018 um 18:48 Uhr

  • @Sajjon Es ist ein zugegebenermaßen alberner (und vielleicht völlig origineller) Ausruf der Verzweiflung ( idioms.thefreedictionary.com/in+the+name+of+god ) vielleicht leicht wie ein zerhackter Eid ( en.wikipedia.org/wiki/Minced_oath ). Ein anderes Beispiel könnte sein: “Was im Namen von Zeus …?!” ( forum.wordreference.com/threads/in-the-name-of-zeus.2132965 )

    – Colin D. Bennett

    15. April 2020 um 16:09 Uhr

Benutzeravatar von piotr
Piotr

Laut snprintf(3)-Manpage.

Die Funktionen snprintf() und vsnprintf() höchstens schreiben size Bytes (einschließlich des nachgestellten Nullbytes (‘\0’)) zu str.

Also, ja, es muss nicht beendet werden, wenn die Größe >= 1 ist.

  • Und Gott sei Dank dafür; dies ist das einzig sinnvolle Design. Der springende Punkt der geprüften Versionen dieser Funktionen soll sein sicherund es wäre schrecklich, wenn Sie den ganzen Abschluss-Malarkey von Hand machen müssten.

    – Kerrek SB

    9. Oktober 2011 um 22:31 Uhr

  • Ich würde empfehlen, es auf der/den von Ihnen verwendeten Plattform(en) zu testen, bevor Sie sich darauf verlassen. Selbst wenn es sollte Schreiben Sie das Null-Byte, ich weiß, dass ich auf Implementierungen gestoßen bin, die dies nicht getan haben (es könnte mit MinGW gewesen sein, das eine ältere MS-Laufzeit verwendet hat).

    – Dimitri

    10. Oktober 2011 um 0:33 Uhr

Benutzeravatar von Jonathan Leffler
Jonathan Leffler

Gemäß dem C-Standard, es sei denn, die Puffergröße ist 0, vsnprintf() und snprintf() null beendet seine Ausgabe.

Das snprintf() Funktion soll äquivalent sein sprintf(), mit dem zusätzlichen Argument n, das die Größe des Puffers angibt, auf den sich s bezieht. Wenn n Null ist, soll nichts geschrieben werden und s kann ein Nullzeiger sein. Andernfalls werden Ausgangsbytes jenseits des n-1. verworfen, anstatt in das Array geschrieben zu werden, und ein Null-Byte wird an das Ende der tatsächlich in das Array geschriebenen Bytes geschrieben.

Wenn Sie also wissen müssen, wie groß ein Puffer zuzuweisen ist, verwenden Sie eine Größe von Null, und Sie können dann einen Nullzeiger als Ziel verwenden. Beachten Sie, dass ich auf die POSIX-Seiten verlinkt habe, aber diese sagen ausdrücklich, dass es keine Abweichungen zwischen Standard C und POSIX geben soll, wenn sie denselben Grund abdecken:

Die auf dieser Referenzseite beschriebene Funktionalität ist an der ISO-C-Norm ausgerichtet. Jeder Konflikt zwischen den hier beschriebenen Anforderungen und dem ISO-C-Standard ist unbeabsichtigt. Dieser Band von POSIX.1-2008 lehnt sich an den ISO-C-Standard an.

Seien Sie vorsichtig bei der Microsoft-Version von vsnprintf(). Es verhält sich definitiv anders als die Standard-C-Version, wenn nicht genügend Platz im Puffer vorhanden ist (es gibt -1 zurück, wo die Standardfunktion die erforderliche Länge zurückgibt). Es ist nicht ganz klar, dass die Microsoft-Version null ihre Ausgabe unter Fehlerbedingungen beendet, während die Standard-C-Version dies tut.

Beachten Sie auch die Antworten zu Verwenden Sie die sicheren Funktionen der TR 24731? (sehen MSDN für die Microsoft-Version des vsprintf_s()) und Mac-Lösung für die sicheren Alternativen zu unsicheren C-Standardbibliotheksfunktionen?

  • oh, böse, daran habe ich noch nie gedacht. Auf der anderen Seite… 🙂

    – Prof. Falken

    9. Oktober 2011 um 22:47 Uhr

  • ah, ich glaube, MS vsprintf() hat mich gebissen, und ich habe das aufgegriffen – 1 Angewohnheit

    – Prof. Falken

    9. Oktober 2011 um 23:00 Uhr

Einige ältere Versionen von SunOS haben seltsame Dinge mit snprintf gemacht und haben möglicherweise die Ausgabe nicht mit NUL beendet und hatten Rückgabewerte, die nicht mit dem übereinstimmten, was alle anderen taten, aber alles, was in den letzten 10 Jahren veröffentlicht wurde, hat das getan, was C99 getan hat sagt.

Benutzeravatar von Robin Kuzmin
Robin Kusmin

Die Mehrdeutigkeit beginnt beim C-Standard selbst. Sowohl C99 als auch C11 haben eine identische Beschreibung von snprintf Funktion. Hier ist die Beschreibung von C99:

7.19.6.5 Die snprintf Funktion
Zusammenfassung

1 #include <stdio.h>
int snprintf(char * restrict s, size_t n, const char * restrict format, ...);

Beschreibung

2 Die snprintf Funktion ist äquivalent zu fprintfaußer dass die Ausgabe in ein Array geschrieben wird (angegeben durch argument s) und nicht zu einem Stream. Wenn n Null ist, nichts geschrieben wird, und s kann ein Nullzeiger sein. Geben Sie andernfalls Zeichen jenseits von aus n-1st werden verworfen, anstatt in das Array geschrieben zu werden, und am Ende der tatsächlich in das Array geschriebenen Zeichen wird ein Nullzeichen geschrieben. Wenn zwischen sich überlappenden Objekten kopiert wird, ist das Verhalten undefiniert.
Kehrt zurück

3 Die snprintf Die Funktion gibt die Anzahl der Zeichen zurück, die geschrieben worden wären n ausreichend groß war, wobei das abschließende Nullzeichen nicht mitgezählt wurde, oder ein negativer Wert, wenn ein Codierungsfehler aufgetreten ist. Somit wurde die nullterminierte Ausgabe genau dann vollständig geschrieben, wenn der zurückgegebene Wert nichtnegativ und kleiner als ist n.

Einerseits der Satz

Andernfalls, Ausgabezeichen jenseits der n-1st werden verworfen anstatt in das Array geschrieben zu werden, und ein Nullzeichen wird an das Ende der tatsächlich geschriebenen Zeichen geschrieben in das Array

sagt, dass
wenn (die s zeigt auf ein 3 Zeichen langes Array und) n ist dann 3 Es werden 2 Zeichen geschrieben, und die Zeichen nach dem 2. werden verworfen; dann ist die Nach diesen 2 wird ein Nullzeichen geschrieben (und das Nullzeichen wird das 3. geschriebene Zeichen sein).

Und das, glaube ich, beantwortet die ursprüngliche Frage.
DIE ANTWORT:

Wenn zwischen sich überlappenden Objekten kopiert wird, ist das Verhalten undefiniert.
Wenn n 0 ist, dann wird nichts auf den Ausgang geschrieben
andernfalls, wenn keine Codierungsfehler aufgetreten sind, die Ausgabe ist IMMER nullterminiert (unabhängig davon, ob die Ausgabe in das Ausgabearray passt oder nicht; Wenn nicht, werden einige Zeichen verworfen, sodass das Ausgabearray niemals überläuft).
andernfalls (wenn Codierungsfehler auftreten) die Ausgabe kann nicht nullterminiert bleiben.

Auf der anderen Seite

Der letzte Satz

Somit wurde die nullterminierte Ausgabe genau dann vollständig geschrieben, wenn der zurückgegebene Wert nichtnegativ und kleiner als ist n

gibt Mehrdeutigkeit (oder mein Englisch ist nicht gut genug). Ich kann diesen Satz auf mindestens zwei Arten interpretieren:
1. Die Ausgabe ist nullterminiert genau dann, wenn der zurückgegebene Wert nichtnegativ ist und weniger als n (was bedeutet, dass wenn der zurückgegebene Wert ist nicht weniger als ndh die Ausgabe (einschließlich des abschließenden Nullzeichens) passt nicht in das Array, dann die Ausgabe ist nicht nullterminiert).
2. Die Ausgabe ist Komplett (es wurden keine Zeichen verworfen) genau dann, wenn der zurückgegebene Wert nichtnegativ ist und weniger als n.


Ich glaube, dass die obige Interpretation 1 DER ANTWORT widerspricht, Missverständnisse und langwierige Diskussionen verursacht. Deshalb der letzte Satz, der die beschreibt snprintf Funktion benötigt eine Änderung, um jegliche Mehrdeutigkeit zu beseitigen (was einen Grund dafür gibt, einen Vorschlag für den C-Sprachstandard zu schreiben).
Das Beispiel der eindeutigen Formulierung kann meines Erachtens entnommen werden http://en.cppreference.com/w/c/io/fprintf (sehen 4)), danke an @”Martin Ba” für den Link.

Siehe auch die Frage “snprintf: Gibt es C-Standard-Vorschläge/-Pläne, die Beschreibung dieser Funktion zu ändern?”.

  • Ihre Deutung 1 erscheint mir überhaupt nicht plausibel. Ich parse diesen Satz als “Die Ausgabe (die übrigens nullterminiert ist) wurde vollständig geschrieben, wenn …”, was ich nur als #2 verstehen kann.

    – zol

    25. Mai 2018 um 4:27 Uhr


  • Die Negation des Satzes „Nullterminierte Ausgabe wurde vollständig geschrieben“ ist „Nullterminierte Ausgabe hat nicht vollständig geschrieben”. Nichts weiter. Der verneinte Satz allein impliziert das nicht irgendetwas geschrieben wurde (dazu gehören unvollständige nullterminierte Ausgaben, unvollständige nicht nullterminierte Ausgaben oder farblose grüne Ideen). An einer anderen Stelle im Standard steht, was genau geschrieben wird, wenn die Ausgabe unvollständig ist, und diese Stelle gibt an, dass die Ausgabe nullterminiert ist, es sei denn, sie ist leer (n == 0).

    – n. 1.8e9-wo-ist-meine-Aktie m.

    25. Mai 2018 um 9:17 Uhr

  • Ihre Deutung 1 erscheint mir überhaupt nicht plausibel. Ich parse diesen Satz als “Die Ausgabe (die übrigens nullterminiert ist) wurde vollständig geschrieben, wenn …”, was ich nur als #2 verstehen kann.

    – zol

    25. Mai 2018 um 4:27 Uhr


  • Die Negation des Satzes „Nullterminierte Ausgabe wurde vollständig geschrieben“ ist „Nullterminierte Ausgabe hat nicht vollständig geschrieben”. Nichts weiter. Der verneinte Satz allein impliziert das nicht irgendetwas geschrieben wurde (dazu gehören unvollständige nullterminierte Ausgaben, unvollständige nicht nullterminierte Ausgaben oder farblose grüne Ideen). An einer anderen Stelle im Standard steht, was genau geschrieben wird, wenn die Ausgabe unvollständig ist, und diese Stelle gibt an, dass die Ausgabe nullterminiert ist, es sei denn, sie ist leer (n == 0).

    – n. 1.8e9-wo-ist-meine-Aktie m.

    25. Mai 2018 um 9:17 Uhr

1420590cookie-checkBeendet snprintf() IMMER null?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy