Warum sollten Sie strncpy anstelle von strcpy verwenden?

81

Bearbeiten: Ich habe die Quelle für das Beispiel hinzugefügt.

Ich bin auf dieses Beispiel gestoßen :

char source[MAX] = "123456789";
char source1[MAX] = "123456789";
char destination[MAX] = "abcdefg";
char destination1[MAX] = "abcdefg";
char *return_string;
int index = 5;

/* This is how strcpy works */
printf("destination is originally = '%s'\n", destination);
return_string = strcpy(destination, source);
printf("after strcpy, dest becomes '%s'\n\n", destination);

/* This is how strncpy works */
printf( "destination1 is originally = '%s'\n", destination1 );
return_string = strncpy( destination1, source1, index );
printf( "After strncpy, destination1 becomes '%s'\n", destination1 );

Welches hat diese Ausgabe erzeugt:

Ziel ist ursprünglich = 'abcdefg'
Nach strcpy wird das Ziel zu '123456789'.

Ziel1 ist ursprünglich = 'abcdefg'
Nach strncpy wird destination1 zu '12345fg'.

Was mich wundert, warum jemand diesen Effekt haben möchte. Es sieht so aus, als wäre es verwirrend. Dieses Programm lässt mich denken, dass Sie mit Tom Bro763 grundsätzlich den Namen einer Person (z. B. Tom Brokaw) kopieren könnten.

Was sind die Vorteile der Verwendung strncpy() gegenüber strcpy() ?

Kredns
quelle
80
Ich denke, Sie wollten fragen: "Warum um alles in der Welt sollte jemand strcpystattdessen verwenden strncpy?"
Sam Harwell
5
Als ich ein TA für einen Programmierkurs im ersten Semester in C war, versicherte ich meinen Studenten, dass jede Verwendung von Methoden wie getlinezu falschen Ergebnissen führen würde, wenn ich sie anhand sorgfältig ausgearbeiteter Eingaben benotete. :)
Sam Harwell
4
Ich denke, Sie haben falsch verstanden, was der Code tatsächlich tut. Schau genauer hin.
Emil H
6
Es ist wirklich schade, dass C nie eine halbwegs anständige Standardbibliothek für Strings bekommen hat.
Starblue
7
Es ist nicht so schade. Ich meine, es hat mich total eingebrochen und höhere Sprachen VIEL mehr Spaß gemacht :)
Carson Myers

Antworten:

95

strncpyBekämpft den Pufferüberlauf, indem Sie eine Länge eingeben müssen. strcpyhängt von einem Trailing ab \0, das möglicherweise nicht immer auftritt.

Zweitens ist es mir ein Rätsel, warum Sie nur 5 Zeichen auf 7 Zeichenfolgen kopieren, aber es führt zu erwartetem Verhalten. Es wird nur über die ersten nZeichen kopiert , wobei ndas dritte Argument ist.

Die nFunktionen werden alle als defensive Codierung gegen Pufferüberläufe verwendet. Bitte verwenden Sie sie anstelle älterer Funktionen, wie z strcpy.

Eric
quelle
47
Siehe lysator.liu.se/c/rat/d11.html : strncpywurde ursprünglich in die C-Bibliothek eingeführt, um Namensfelder mit fester Länge in Strukturen wie Verzeichniseinträgen zu behandeln. Solche Felder werden nicht wie Zeichenfolgen verwendet: Die nachfolgende Null ist für ein Feld mit maximaler Länge nicht erforderlich, und das Setzen von nachgestellten Bytes für kürzere Namen auf Null gewährleistet effiziente feldweise Vergleiche. strncpyist nicht ursprünglich ein "begrenzter Strcpy", und der Ausschuss hat es vorgezogen, bestehende Praktiken anzuerkennen, anstatt die Funktion zu ändern, um sie besser für eine solche Verwendung geeignet zu machen.
Sinan Ünür
34
Ich bin mir nicht sicher, warum dies viele Stimmen nach oben bringt - strncpy war nie als sicherere Alternative zu strcpy gedacht und ist in der Tat nicht sicherer, da es die Zeichenfolge nicht auf Null setzt. Es hat auch eine andere Funktionalität, da es die mitgelieferte Länge mit NUL-Zeichen auffüllt. Wie das Café in seiner Antwort sagt, dient es zum Überschreiben von Zeichenfolgen in einem Array mit fester Größe.
Ölmessstab
24
Die Tatsache bleibt , dass strncpyist nicht eine sicherere Version strcpy.
Sinan Ünür
7
@ Sinan: Ich habe nie gesagt, dass es sicherer ist. Es ist defensiv. Es zwingt dich, eine Länge einzugeben, was dich dazu bringt, darüber nachzudenken, was du tust. Es gibt bessere Lösungen, aber die Tatsache bleibt, dass die Leute sie verwenden würden (und tun), strncpyanstatt strcpyweil es eine viel defensivere Funktion ist ... was ich gesagt habe.
Eric
8
Die n Funktionen werden alle als defensive Codierung gegen Pufferüberläufe verwendet. Bitte verwenden Sie sie anstelle älterer Funktionen wie strcpy. Dies gilt für snprintf, ist aber irrelevant strncatund für völlig falsch strncpy. Wie konnte diese Antwort jemals so viele positive Stimmen bekommen? Es zeigt, wie schlimm die Situation in Bezug auf diese Scheinfunktion ist. Die Verwendung ist nicht defensiv: In den meisten Situationen versteht der Programmierer seine Semantik nicht und erstellt eine möglicherweise nicht nullterminierte Zeichenfolge.
Chqrlie
176

Die strncpy()Funktion wurde unter Berücksichtigung eines ganz bestimmten Problems entwickelt: Manipulieren von Zeichenfolgen, die in der Art der ursprünglichen UNIX-Verzeichniseinträge gespeichert sind. Diese verwendeten ein Array mit fester Größe, und ein Nullterminator wurde nur verwendet, wenn der Dateiname kürzer als das Array war.

Das ist es, was hinter den beiden Kuriositäten steckt strncpy():

  • Es setzt keinen Nullterminator auf das Ziel, wenn es vollständig gefüllt ist. und
  • Es füllt das Ziel immer vollständig aus, bei Bedarf mit Nuls.

Für ein "sichereres strcpy()" ist es besser, wenn Sie Folgendes verwenden strncat():

if (dest_size > 0)
{
    dest[0] = '\0';
    strncat(dest, source, dest_size - 1);
}

Dadurch wird das Ergebnis immer nicht beendet und es wird nicht mehr als erforderlich kopiert.

caf
quelle
Aber natürlich ist strncpy auch nicht immer das, was Sie wollen: strncpy akzeptiert die maximale Anzahl der hinzuzufügenden Zeichen und nicht die Größe des Zielpuffers ... Aber das ist nur eine Kleinigkeit, wird also wahrscheinlich kein Problem sein, es sei denn, Sie Ich versuche, eine Zeichenfolge mit einer anderen zu verketten.
David Wolever
Ich kannte den Grund dafür nicht und es ist sehr relevant für das, woran ich arbeite.
Matt Joiner
Die Funktion strncpy () dient zum Speichern von Zeichenfolgen im nullgefüllten Format fester Länge. Ein solches Format wurde für die ursprünglichen Unix-Verzeichniseinträge verwendet, wird aber auch an unzähligen anderen Stellen verwendet, da eine Zeichenfolge von 0 bis N Bytes in N Bytes Speicher gespeichert werden kann. Noch heute verwenden viele Datenbanken nullgefüllte Zeichenfolgen in ihren Zeichenfolgenfeldern fester Länge. Die Verwechslung mit strncpy () beruht auf der Tatsache, dass Zeichenfolgen in das FLNP-Format konvertiert werden. Wenn man einen FLNP-String braucht, ist das wunderbar. Wenn eine nullterminierte Zeichenfolge benötigt wird, muss die Terminierung selbst bereitgestellt werden.
Supercat
Ich verstehe nicht "akzeptiert die maximale Anzahl von Zeichen, die hinzugefügt werden sollen, und nicht die Größe des Zielpuffers" - es akzeptiert auch nicht, es akzeptiert eine Ganzzahl: Es berührt genau n Zeichen des Ziels und nicht mehr. Es untersucht (und kopiert) Zeichen aus der Quelle, bis es n kopiert hat oder bis es auf ein NUL stößt - es untersucht nie mehr als n Zeichen aus der Quelle.
Spike0xff
3
@snr: strncat()Verkettet die Quellzeichenfolge mit dem Ende der Zielzeichenfolge . Wir wollen nur die Quellzeichenfolge in das Ziel kopieren, also setzen wir das Ziel zuerst auf die leere Zeichenfolge - das ist, was dest[0] = '\0';tut.
Café
34

Obwohl ich die Absicht dahinter kenne strncpy, ist es keine wirklich gute Funktion. Vermeiden Sie beides. Raymond Chen erklärt .

Persönlich ist meine Schlussfolgerung einfach zu vermeiden strncpyund alle seine Freunde, wenn Sie mit nullterminierten Zeichenfolgen zu tun haben. Trotz des "str" ​​im Namen erzeugen diese Funktionen keine nullterminierten Zeichenfolgen. Sie konvertieren eine nullterminierte Zeichenfolge in einen Rohzeichenpuffer. Die Verwendung dieser Zeichenfolgen, bei denen eine nullterminierte Zeichenfolge erwartet wird, da der zweite Puffer eindeutig falsch ist. Sie erhalten nicht nur keine ordnungsgemäße Nullterminierung, wenn die Quelle zu lang ist, sondern wenn die Quelle kurz ist, erhalten Sie unnötige Nullauffüllung.

Siehe auch Warum ist strncpy unsicher?

Sinan Ünür
quelle
27

strncpy ist NICHT sicherer als strcpy, es tauscht nur eine Art von Fehlern mit einer anderen. In C müssen Sie beim Umgang mit C-Strings die Größe Ihrer Puffer kennen, daran führt kein Weg vorbei. strncpy war für das von anderen erwähnte Verzeichnis gerechtfertigt, aber ansonsten sollten Sie es niemals verwenden:

  • Wenn Sie die Länge Ihres Strings und Puffers kennen, warum sollten Sie strncpy verwenden? Es ist bestenfalls eine Verschwendung von Rechenleistung (Hinzufügen von nutzlosen 0)
  • Wenn Sie die Längen nicht kennen, riskieren Sie, Ihre Zeichenfolgen stillschweigend abzuschneiden, was nicht viel besser ist als ein Pufferüberlauf
David Cournapeau
quelle
Ich denke, dies ist eine gute Beschreibung für strncpy, also habe ich darüber abgestimmt. strncpy hat seine eigenen Probleme. Ich denke, das ist der Grund, warum zB glib seine eigenen Erweiterungen hat. Und ja, es ist bedauerlich, dass Sie als Programmierer die Größe aller Arrays kennen müssen. Die Entscheidung mit 0 terminierten char-Arrays als String hat uns alle
Friedrich
1
Null-aufgefüllte Zeichenfolgen sind ziemlich häufig, wenn Daten in Dateien mit festem Format gespeichert werden. Die Popularität von Dingen wie Datenbank-Engines und XML sowie die sich ändernden Benutzererwartungen haben freilich dazu geführt, dass Dateien mit festem Format weniger häufig sind als vor 20 Jahren. Dennoch sind solche Dateien häufig das zeiteffizienteste Mittel zum Speichern von Daten. Außer wenn es große Unterschiede zwischen der erwarteten und der maximalen Länge von Daten in einem Datensatz gibt, ist es viel schneller, einen Datensatz als einen einzelnen Block zu lesen, der einige nicht verwendete Daten enthält, als einen Datensatz zu lesen, der in mehrere Blöcke unterteilt ist.
Supercat
Ich habe gerade die Wartung des Legacy-Codes übernommen, der g_strlcpy () verwendet hat, so dass die Ineffizienzen beim Auffüllen nicht leiden. Die Anzahl der übertragenen Bytes wurde jedoch NICHT beibehalten, sodass der Code das Ergebnis stillschweigend abschneidet.
user2548100
21

Was Sie suchen, ist die Funktion, strlcpy()die den String immer mit 0 beendet und den Puffer initialisiert. Es ist auch in der Lage, Überläufe zu erkennen. Einziges Problem, es ist nicht (wirklich) portabel und nur auf einigen Systemen (BSD, Solaris) vorhanden. Das Problem mit dieser Funktion ist, dass sie eine weitere Dose Würmer öffnet, wie aus den Diskussionen auf http://en.wikipedia.org/wiki/Strlcpy hervorgeht

Meine persönliche Meinung ist, dass es weitaus nützlicher ist als strncpy()und strcpy(). Es hat eine bessere Leistung und ist ein guter Begleiter zu snprintf(). Für Plattformen, die es nicht haben, ist es relativ einfach zu implementieren. (Für die Entwicklungsphase einer Anwendung ersetze ich diese beiden Funktionen ( snprintf()undstrlcpy() ) durch eine Überfüllungsversion, die das Programm bei Pufferüberläufen oder -kürzungen brutal abbricht. Dadurch können die schlimmsten Straftäter schnell erkannt werden. Insbesondere, wenn Sie an einer Codebasis von jemand anderem arbeiten .

EDIT: strlcpy()kann einfach implementiert werden:

size_t strlcpy(char *dst, const char *src, size_t dstsize)
{
  size_t len = strlen(src);
  if(dstsize) {
    size_t bl = (len < dstsize-1 ? len : dstsize-1);
    ((char*)memcpy(dst, src, bl))[bl] = 0;
  }
  return len;
}
Patrick Schlüter
quelle
3
Sie könnten schreiben, dass strlcpy unter so ziemlich allem außer Linux und Windows verfügbar ist! Es ist jedoch BSD-lizenziert, sodass Sie es einfach in eine Ihrer Bibliotheken ablegen und von dort aus verwenden können.
Michael van der Westhuizen
Möglicherweise möchten Sie einen Test hinzufügen dstsize > 0und nichts tun, wenn dies nicht der Fall ist.
Chqrlie
Ja, du hast Recht. Ich werde den Scheck hinzufügen, da ohne ihn dstsizedie memcpyLänge lendes Zielpuffers ausgelöst und übergelaufen wird.
Patrick Schlüter
Plus eins für die Förderung guter Lösungen. Mehr Menschen müssen über strlcpy Bescheid wissen, weil jeder es immer wieder neu erfindet.
rsp
@MichaelvanderWesthuizen Es ist unter Linux verfügbar, nur nicht in glibc. Siehe meine Antworten für weitere Informationen (1) (2) (3)
rsp
3

Die strncpy()Funktion ist sicherer: Sie müssen die maximale Länge übergeben, die der Zielpuffer akzeptieren kann. Andernfalls kann es vorkommen, dass die Quellzeichenfolge nicht korrekt mit 0 abgeschlossen ist. In diesem Fall wird diestrcpy() Funktion mehr Zeichen in das Ziel schreiben und alles beschädigen, was sich nach dem Zielpuffer im Speicher befindet. Dies ist das Pufferüberlaufproblem, das bei vielen Exploits verwendet wird

Auch für POSIX-API-Funktionen, bei read()denen die abschließende 0 nicht in den Puffer gestellt wird, sondern die Anzahl der gelesenen Bytes zurückgegeben wird, setzen Sie die 0 entweder manuell oder kopieren sie mit strncpy().

In Ihrem Beispielcode indexhandelt es sich eigentlich nicht um einen Index, sondern um a count- es gibt an, wie viele Zeichen höchstens von der Quelle zum Ziel kopiert werden sollen. Wenn unter den ersten n Bytes der Quelle kein Nullbyte vorhanden ist, wird die im Ziel platzierte Zeichenfolge nicht mit Null terminiert

CsTamas
quelle
1

strncpy füllt das Ziel mit '\ 0' für die Größe der Quelle, obwohl die Größe des Ziels kleiner ist ....

Manpage:

Wenn die Länge von src kleiner als n ist, füllt strncpy () den Rest von dest mit Null-Bytes auf.

und nicht nur der Rest ... auch danach, bis n Zeichen erreicht sind. Und so kommt es zu einem Überlauf ... (siehe Manpage-Implementierung)

Jeronimo
quelle
3
strncpy füllt das Ziel mit '\ 0' für die Größe der Quelle, obwohl die Größe des Ziels kleiner ist ... Ich fürchte, diese Aussage ist falsch und verwirrend: strncpyfüllt das Ziel mit '\ 0' für die Größenargument, wenn die Länge der Quelle geringer ist. Das Größenargument ist nicht die Größe der Quelle, nicht die maximale Anzahl von Zeichen, die aus der Quelle kopiert werden sollen, da es sich strncatum die Größe des Ziels handelt.
Chqrlie
@chqrlie: Genau. Ein Vorteil strncpygegenüber anderen Kopiervorgängen besteht darin, dass garantiert wird, dass das gesamte Ziel geschrieben wird. Da Compiler möglicherweise versuchen, beim Kopieren von Strukturen, die einige unbestimmte Werte enthalten, "kreativ" zu werden, kann es am einfachsten sein, sicherzustellen, dass alle Zeichenarrays in Strukturen vollständig geschrieben werden, um "Überraschungen" zu vermeiden.
Supercat
@ Supercat: ein sehr kleiner Vorteil für diesen speziellen Fall ... aber das Ziel muss nach dem Aufruf gepatcht werden, strncpyum eine Null-Beendigung zu gewährleisten: strncpy(dest, src, dest_size)[dest_size - 1] = '\0';
chqrlie
@chqrlie: Ob ein nachfolgendes Nullbyte erforderlich wäre oder nicht, hängt davon ab, was die Daten darstellen sollen. Die Verwendung von mit Nullen aufgefüllten Daten anstelle von mit Null abgeschlossenen Daten innerhalb einer Struktur ist nicht mehr so ​​häufig wie früher. Wenn jedoch beispielsweise in einem Objektdateiformat 8-Byte-Abschnittsnamen verwendet werden, kann die Funktion char[8]innerhalb einer Struktur die Dinge in den Griff bekommen bis zu 8 Zeichen sind möglicherweise besser als die Verwendung von a, können char[8]jedoch nur 7 Zeichen verarbeiten oder müssen eine Zeichenfolge in den char[9]Puffer und dann memcpyan das Ziel kopieren .
Supercat
@chqrlie: Der meiste Code, der Dinge mit Strings macht, sollte wissen, wie lang sie sein könnten, und sollte nicht blind mit charZeigern laufen, bis sie eine Null erreichen. Das einzige, wofür nullterminierte Zeichenfolgen wirklich gut sind, sind Zeichenfolgenliterale, und selbst dort wäre ein mit variabler Länge codiertes Präfix wahrscheinlich besser. Für fast alles andere wäre es besser, Zeichenfolgen entweder mit einer Länge vorangestellt zu haben oder ein spezielles Präfix zu haben, das anzeigt, dass das char*wirklich so etwas ist struct stringInfo {char header[4]; char *realData; size_t length; size_t size;}.
Supercat
-1

Dies kann in vielen anderen Szenarien verwendet werden, in denen Sie nur einen Teil Ihrer ursprünglichen Zeichenfolge an das Ziel kopieren müssen. Mit strncpy () können Sie einen begrenzten Teil der ursprünglichen Zeichenfolge kopieren, im Gegensatz zu strcpy (). Ich sehe, dass der Code, den Sie erstellt haben, von publib.boulder.ibm.com stammt .

ARV
quelle
-1

Das hängt von unserer Anforderung ab. Für Windows-Benutzer

Wir verwenden strncpy immer dann, wenn wir nicht die gesamte Zeichenfolge oder nur n Zeichen kopieren möchten. Strcpy kopiert jedoch die gesamte Zeichenfolge einschließlich der Beendigung des Nullzeichens.

Über diese Links erfahren Sie mehr über strcpy und strncpy und wo wir sie verwenden können.

über strcpy

über strncpy

Prakash
quelle
-8

strncpy ist eine sicherere Version von strcpy. Tatsächlich sollten Sie strcpy niemals verwenden, da es eine potenzielle Sicherheitsanfälligkeit bezüglich Pufferüberlauf darstellt, die Ihr System für alle Arten von Angriffen anfällig macht

bashmohandes
quelle
6
Siehe lysator.liu.se/c/rat/d11.html : Die Funktion strncpy strncpy wurde ursprünglich in die C-Bibliothek eingeführt, um Namensfelder mit fester Länge in Strukturen wie Verzeichniseinträgen zu verarbeiten. Solche Felder werden nicht wie Zeichenfolgen verwendet: Die nachfolgende Null ist für ein Feld mit maximaler Länge nicht erforderlich, und das Setzen von nachfolgenden Bytes für kürzere Namen auf Null gewährleistet effiziente feldweise Vergleiche. strncpy ist nicht ursprünglich ein "begrenzter strcpy", und der Ausschuss hat es vorgezogen, bestehende Praktiken anzuerkennen, anstatt die Funktion zu ändern, um sie besser für eine solche Verwendung geeignet zu machen.
Sinan Ünür