strncpy führt zu Segmentierungsfehler

Ich mache gerade mit Strncpy herum.

Mein Programm sieht so aus

typedef struct
{
    char from_str[10];
}test;

main ()
{

    test     s1;
    memset(&s1,0,sizeof(test));
    char       src[10]="himansh";
    char       dest[10];

    memset(dest,0,10);
    src[3]='\0';

    printf("src is %s and strlen is %d \n",
            src,strlen(src));

    fflush(stdout);

    strncpy(s1.from_str,src,100);

    printf("s1.from_str is %s , src is %s \n",
            s1.from_str,src);
    return 1;

}

Hier, bevor ich strncpy habe ich ein "\ 0" -Zeichen in "src" Zeichenfolge hinzugefügt, Länge von "src" Zeichenfolge wird 3, Ziel-Array ist der Größe 10. Aber in strncpy habe ich die Anzahl der zu kopierenden Bytes als 100 .

Das bedeutet, dass meine Quellzeichenfolge NULL beendet ist. Jetzt sollte strncpy wie jede String-Funktion versuchen, nur 3 Byte zu kopieren, selbst wenn die Anzahl der von mir bereitgestellten Bytes mehr als 3 (in diesem Fall 100) beträgt. Es tut das, aber ich bekomme auch einen Segmentierungsfehler.

Mein Ergebnis ist unten gezeigt

src is him and strlen is 3
s1.from_str is him , src is him
Segmentation fault (core dumped)

Warum passiert dieser Segmentierungsfehler hier?

Kann mir hier jemand helfen?

8
nl ja ru
Was du denkst, dass es versuchen sollte, und was es tut, sind zwei völlig verschiedene Dinge.
hinzugefügt der Autor Brian Roach, Quelle

4 Antworten

Ich könnte Sie auf Man-Pages, Websites usw. hinweisen, aber letztendlich zählt der C-Standard selbst. Als Teil der Standard-Laufzeitbibliothek sind die Verwendung und das Verhalten in C99-§7.23.2.4 definiert als:

#include 
char *strncpy(char * restrict s1,
      const char * restrict s2,
      size_t n);

Beschreibung   Die strncpy -Funktion kopiert nicht mehr als n Zeichen (Zeichen, die einem Nullzeichen folgen, werden nicht kopiert) von dem Array, auf das s2 zeigt, auf das Array, auf das gezeigt wird   s1. Wenn zwischen überlappenden Objekten kopiert wird, ist das Verhalten nicht definiert.   Wenn das Array, auf das s2 zeigt, eine Zeichenfolge ist, die kürzer als n Zeichen ist, werden Nullzeichen an die Kopie in dem Array angehängt, auf das s1 zeigt, bis alle n Zeichen geschrieben wurden.

     

Gibt zurück   Die Funktion strncpy gibt den Wert von s1 zurück.

Es gibt signifikante implizierte Informationen hier, die wichtigste ist: strncpy() wird NOT die Zielzeichenfolge mit einem Nullzeichen beenden, wenn die Quellzeichenfolge angegeben wird Die Länge (einschließlich des Nullzeichen-Abschlusszeichens) erfüllt oder überschreitet die angegebene Zielpufferlänge.

Darüber hinaus verwirrt es mich weiterhin, obwohl es im Standard (siehe oben) klar spezifiziert ist, wie viele Ingenieure NICHT wissen, dass strncpy() den Ziel-String-Puffer mit füllt NULL-Zeichen, bis die angegebene Länge n erreicht ist, wenn die Länge der Quellzeichenfolge kleiner als die Zielpuffergröße ist. Daraus ergibt sich folgende unausweichliche Schlussfolgerung:

Die strncpy() API schreibt IMMER n -Zeichen an die Adresse, auf die der Zielpuffer verweist.

In deinem Fall, weil der Zielpuffer nur 10 Zeichen breit ist, schreibst du 90 zusätzliche Zeichen hinter das definierte Ende des Schreibspeichers und gehst damit in das Land des undefinierten Verhaltens .

An diesem Punkt müssen Sie sich fragen: "Also, was ist der Gebrauch?" Dort ist ein wohl fundamentaler Anwendungsfall. Es ermöglicht Ihnen, bis zu n Zeichen in den Zielpuffer zu kopieren, mit der Voraussagbarkeit, dass Sie keine n Zeichen überschreiben. Zeitraum. Letztendlich möchten Sie jedoch eine Null-terminierte Zeichenfolge, also die richtige Verwendung ist dies:

char dst[ N ]; 
strncpy(dst, src, N-1);
dst[N-1] = 0;

Dabei steht N für die Länge des dst -Puffers in Zeichen und ist größer als oder gleich 1 . Beachten Sie, dass dst genau so gut ein dynamisch zugewiesener Speicherzeiger sein könnte:

char *dst = malloc( N * sizeof(char) ); 
strncpy(dst, src, N-1);
dst[N-1] = 0;

Mit dem obigen haben Sie immer eine Null-terminierte Zeichenfolge bei dst . Wenn die Quellzeichenfolge length kleiner als die angegebene Zielpufferlänge ist, füllt strncpy() den Rest des Puffers mit Nullzeichen bis zur Gesamtzahl der Quellzeichen -copied + tail-fulled-null-Zeichen entspricht n und die abschließende Anweisung ist redundant. Wenn die Länge der Quellzeichenfolge gleich oder größer als die Zielpufferlänge ist, beendet strncpy() das Kopieren, sobald N-1 Zeichen erreicht sind , und die letzte Anweisung setzt am Ende des Puffers ein Nullzeichen. Dies führt zu einer "cut-down" Präfix-Zeichenfolge der ursprünglichen Quelle, aber am wichtigsten, es stellt sicher, dass Sie die Grenzen des Zielpuffers NICHT überschreiten mit einem späteren String-API-Aufruf, der nach einem Terminator sucht.

Die Nützlichkeit der obigen Technik ist immer umstritten. Ich bin ein C ++ - Typ, also rettet std :: string mein Happy-Self vor all dem Wahnsinn. Aber die Realität ist folgende: Manchmal ist es Ihnen egal, ob src nicht in seine Gesamtheit nach dst kopiert wird; manchmal nicht. Der Nutzen ist situationsabhängig stark. Um String-Daten in einer UI darzustellen, spielt dies (wahrscheinlich) keine Rolle. Zum Kopieren einer Zeichenfolge, die für kritische Daten verwendet werden soll, wird eine partielle Präfix-Teilzeichenfolge nicht akzeptiert. Wenn die Polizei einen Haftbefehl gegen "Joseph Johnson Jr." ausstellt, wird es einige Erklärungen geben, wenn sein Vater ("Joseph Johnson") ins Gefängnis kommt, weil der Namenspuffer der Warrant-Issue-Software nur 15 Zeichen enthielt .

All dies gesagt, Ihre Segmentierung Fehler kommt zu dieser Aussage:

strncpy(s1.from_str,src, 100);//length parameter is wrong.

Erinnern Sie sich an die obige fett gedruckte Anweisung: " strncpy() schreibt IMMER n Zeichen an die Adresse, auf die der Zielpuffer verweist." . Dies bedeutet, dass der obige Code immer 100 Zeichen in den Zielpuffer schreibt, was in Ihrem Fall nur 10 Zeichen breit ist, also undefiniertes Verhalten und wahrscheinlich ker-boom .

Beheben Sie dies, indem Sie Folgendes tun, wenn der Zielpuffer ein Zeichenfeld fester Länge ist:

strncpy(s1.from_str,src, sizeof(s1.from_str)/sizeof(s1.from_str[0])-1);
s1.from_str[ sizeof(s1.from_str)/sizeof(s1.from_str[0])-1 ] = 0;

Sehen Sie sich die vorherige Verwendung an, wie Sie dies für dynamische Zeichenketten der Länge `N chars tun.

13
hinzugefügt
strncpy dient zur vorhersagbaren Ablage in Feldern mit fester Größe. Es ist nicht eine "String-Handling-Funktion".
hinzugefügt der Autor vonbrand, Quelle
thnx @WhozCraig .. sehr detailliert und beschreibend .. Ich habe nur mit strncpy herumalbern, ich weiß, wie man strncpy sicher benutzt. Ich wollte nur wissen, ob 'src' string NULL terminiert ist und wieviele Zeichen in 'dest' String kopiert werden, wenn 'n' mehr als die Größe von 'src' string und Größe von 'dest' string groß genug ist 'src' string aber kleiner als 'n'. MAN Seite Beschreibung von Strncpy war mir nicht klar genug.
hinzugefügt der Autor Himanshu Gupta, Quelle

From http://www.cplusplus.com/reference/cstring/strncpy/

char * strncpy (char * Ziel, const char * Quelle, size_t num   );

     

Zeichen aus Zeichenfolge kopieren Kopiert die ersten Zahlen der Quelle   zum Ziel. Wenn das Ende der Quelle C-Zeichenfolge (die signalisiert wird   durch ein Null-Zeichen) gefunden, bevor num Zeichen kopiert wurden,   Das Ziel wird mit Nullen aufgefüllt, bis eine Gesamtzahl von Zeichen vorhanden ist   wurde darauf geschrieben.

Obwohl die Länge der Quellzeichenfolge kleiner als die Größe der Zielpuffergröße ist, versucht sie dennoch, aufgrund des Weiterleitungsverhaltens von strncpy, den Rest der Zeichen über die Zielpuffergröße hinaus zu überlagern, was einen Segmentierungsfehler verursacht

Anstatt mehr als 100 Zeichen zu kopieren, sollte die Größe der maximal zulässigen Größe im Zielpuffer entsprechen. Sie hätten also schreiben können

strncpy(s1.from_str,src,sizeof(s1.from_str)/sizeof(s1.from_str[0]) - 1); Actual size -1 to accomodate the null terminator 

oder besser, ein _countof Makro zu schreiben

#define _countof(s) (sizeof(s)/sizeof(s[0]))
................
strncpy(s1.from_str,src,_countof(s1.from_str) - 1);
5
hinzugefügt
@HimanshuGupta: Wenn die Antwort geholfen hat, versuchen Sie, die Antwort zu verbessern und zu akzeptieren
hinzugefügt der Autor Abhijit, Quelle
@HimanshuGupta: Schreiben über den zugewiesenen Speicher hinaus ist ein undefiniertes Verhalten (UB) und Segmentation Fault ist eine der vielen UBs einschließlich Dämonen, die aus deiner Nase fliegen
hinzugefügt der Autor Abhijit, Quelle
@HimanshuGupta: Was unter der Haube passiert, ist, dass strncpy darüber schreibt, was nach dem kopierten Ort auch kommt. Was das ist, hängt von den Variablen ab, die Sie definieren, wie Ihr Compiler Daten im Speicher verteilt, wenn er einige Variablen löscht, weil er weiß, dass sie nicht verwendet werden (wenn ja, hängt von den Optimierungsflags ab), ... und schließlich Auf was genau wird überschrieben (Rücksendeadresse?) und was die dort geschriebenen Bytes interpretiert werden als (wenn Rückadresse, könnte es eine illegale Befehlsadresse sein, lande mitten in tiefer Do-Do, oder sei harmlos).
hinzugefügt der Autor vonbrand, Quelle
Dieser seg-Fehler tritt in einem LINUX-Rechner auf, aber nicht in einem anderen UNIX-Rechner. Warum?
hinzugefügt der Autor Himanshu Gupta, Quelle
thnx @Abhijit ..
hinzugefügt der Autor Himanshu Gupta, Quelle

See: http://www.manpagez.com/man/3/strncpy/

Die Funktionen stpncpy() und strncpy() kopieren maximal n Zeichen von s2      in s1. Wenn s2 weniger als n Zeichen lang ist, ist der Rest von s1      gefüllt mit `\ 0 'Zeichen. Andernfalls wird s1 nicht beendet.

Der Rest ist gefüllt ....

So:

strncpy( s1.from_str, src, 10 );
1
hinzugefügt
@icepack, weil 90 Bytes nach from_str (Speicher, der Ihnen nicht gehört) überschrieben werden. Und dann kommt es auf die Compilereinstellungen/OS an. ... was passiert
hinzugefügt der Autor Mario The Spoon, Quelle
Wie verhält es sich also mit der Frage?
hinzugefügt der Autor SomeWittyUsername, Quelle
Das ist richtig, aber die Antwort gibt nichts davon an, es ist nur eine Kopie von der Referenz
hinzugefügt der Autor SomeWittyUsername, Quelle
Ich mache nur herumalbern. Dies geschieht auf einem LINUX-Rechner, aber ich bekomme keinen Seg-Fehler auf einem anderen UNIX-Rechner.
hinzugefügt der Autor Himanshu Gupta, Quelle
Thnx @icepack ..
hinzugefügt der Autor Himanshu Gupta, Quelle
Thnx @Mario Der Löffel
hinzugefügt der Autor Himanshu Gupta, Quelle
aber hier ist die Größe von src string geringer als die von dest string und n ist mehr als die Größe von src und dest string.
hinzugefügt der Autor Himanshu Gupta, Quelle

strncpy (s1.from_str, src, 100);

Warum verwenden Sie 100 in Ihrer Funktion, from_str und src haben beide 10 aufeinanderfolgende Bytes zugewiesen, aber Sie kopieren 100 Bytes, was zu seg führt. Fehler.

benutze das so,

strncpy (s1.from_str, src, 10);

0
hinzugefügt
Da dies zu undefiniertem Verhalten führt, erhalten Sie möglicherweise keinen seg-Fehler auf einem anderen LINUX-Rechner, aber es ist besser, in Ihrem Fall strcpy zu verwenden.
hinzugefügt der Autor Adeel Ahmed, Quelle
Ich mache nur herumalbern. Dies geschieht auf einem LINUX-Rechner, aber ich bekomme keinen Seg-Fehler auf einem anderen UNIX-Rechner.
hinzugefügt der Autor Himanshu Gupta, Quelle
Thnx @ Adeel Ahmed
hinzugefügt der Autor Himanshu Gupta, Quelle