Wie zu beheben strcpy so, dass es erkennt überlappende strings
In einem interview wurde ich gebeten, zu schreiben, der eine Implementierung von strcpy
und dann fixieren Sie es so, dass es richtig behandelt überlappende strings. Meine Implementierung ist unten und es ist sehr naiv. Wie kann ich es beheben, so dass:
- Es erkennt überlappende strings und
- nach erkennen, wie gehen wir mit der überlappung und gehen?
char* my_strcpy(char *a, char *b) {
if (a == NULL || b == NULL) {
return NULL;
}
if (a > b) {
//we have an overlap?
return NULL;
}
char *n = a;
while (*b != '\0') {
*a = *b;
a++;
b++;
}
*a = '\0';
return n;
}
int main(int argc, char *argv[])
{
char str1[] = "wazzupdude";
char *after_cpy = my_strcpy(str1 + 2, str1);
return 0;
}
EDIT:
Also eine mögliche Umsetzung auf der Grundlage @Secure Antwort ist:
char* my_strcpy(char *a, char *b) {
if (a == NULL || b == NULL) {
return NULL;
}
memmove(a, b, strlen(b) + 1);
return a;
}
Wenn wir verlassen uns nicht auf memmove
, dann
char* my_strcpy(char *a, char *b) {
if (a == NULL || b == NULL) {
return NULL;
}
if (a == b) {
return a;
}
//case1: b is placed further in the memory
if ( a <= b && a + strlen(a) > b ) {
char *n = a;
while(*b != '\0') {
*a = *b;
a++; b++;
}
*a = '\0';
return n;
}
//case 2: a is further in memory
else if ( b <= a && b + strlen(b) > a ) {
char *src = b + strlen(b) - 1; //src points to end of b
char *dest = a;
while(src != b) {
*dest = *src;
dest--; src--; //not sure about this..
}
*a = '\0';
return a;
}
}
- Wie ist
a > b
sollen "erkennen, ein überlappen"? Es werden lediglich tests der beiden Adressen. - Kann man zwei Kopien: kopieren Sie zuerst zu einem lokalen Puffer, keine chance zu überlappen, dann aus dem lokalen Puffer auf das Ziel zu.
- könnte man, aber dann
my_strcpy
müsste erlaubt sein, zu scheitern ENOMEM. - stimmt - "Es gibt kein solches Ding wie ein freies Mittagessen gibt"; obwohl dabei zwei Kopien ist sehr weit von einem kostenlosen Mittagessen in den ersten Platz 🙂
- In Bezug auf Ihre Bearbeitung, wie ein interviewer meine nächste Frage wäre: Warum sollte man sich nicht darauf verlassen, memmove, und stattdessen den Handel one-liner gegen einen wartbaren Zeiger Umgang mit Chaos?
- eigentlich...möchte ich Erstens verlassen sich auf memmove und wenn der interviewer fragt nach der blutigen details oder besteht kann ich nicht verwenden Sie memmove ich würde geben eine detaillierte Umsetzung...btw..ist die Umsetzung oben korrigieren?
- Richtig? Ich Bezug auf die Portabilität, wie gesagt, Sie sind Berufung auf Undefiniertes Verhalten, wenn die zwei Zeiger sind aus verschiedenen arrays. Es gibt einen offensichtlichen Fehler, bist du mit
strlen(a)
, aber das ist einfach zu lösen.*dest = '\0';
legt das erste byte desa
, nicht auf das Letzte byte. Aber am wichtigsten ist, Sie ' ve verpasst den Fall, dass die strings sich nicht überlappen... - Für den Fall, wo Sie sich nicht überlappen würden, werden umgesetzt in der gleichen Weise, wie case1. Allerdings bin ich nicht in der Lage zu visualisieren, der zweite Fall(eine ist weiter im Speicher)..daher der Fehler codieren.Ich machte einige änderungen, aber ich werde es zu schätzen wissen wenn Sie mir helfen können visualisieren, Fall 2
- Ich habe bearbeitet Sie meine Antwort mit einer Visualisierung der Fall ist 2.
- Hinweis:
if(a == NULL || b == NULL){ return NULL; }
ist nicht erforderlich, wenna
,b
Punkt-zu-C-strings. - Sollte nicht die "char *dest = a;" am Ende der entsprechenden Ziel-und count-down von dort aus, wenn Sie den Countdown von der aktuellen position, bist du nicht Puffer-unter-fließt?
Du musst angemeldet sein, um einen Kommentar abzugeben.
Gibt es keinen portablen Weg, dies zu erkennen. Sie haben zu tun, Zeiger-Vergleiche, und diese sind nur definiert innerhalb des gleichen Objekts. I. e. wenn die beiden strings nicht überschneiden und sind in der Tat verschiedene Objekte, dann die Zeiger-Vergleiche geben Ihnen Undefiniertes Verhalten.
Ich würde das standard-Bibliothek behandelt diese, durch die Verwendung
memmove(a, b, strlen(b) + 1)
.EDIT:
Als Steve Jessop wies darauf hin, in den Kommentaren, es ist eigentlich eine portable, aber langsam Möglichkeit zu erkennen, überschneiden sich in diesem Fall. Vergleichen Sie jede Adresse innerhalb von b mit der ersten und letzten Adresse eines für die Gleichstellung. Der Geschlechter-Vergleich mit
==
ist immer gut definiert.So, Sie haben so etwas wie dieses:
EDIT 2: Visualisierung von Fall 2
Haben Sie etwas wie das folgende array und Zeiger:
Beachten Sie, dass
b + strlen(b)
Ergebnisse in einen Zeiger auf das abschließende \0. Start hinter sich, was Sie brauchen zusätzliche Behandlung der Grenzfälle. Es ist gültig bis legen Sie den Zeiger dort, nur können Sie nicht dereferenzieren Sie.Nun die copy loop, die Kopien der \0, zu.
Den ersten Schritt bietet dieser:
Und so weiter, bis
src
landet gleichb
:Wenn Sie wollen, dass es ein bisschen mehr hackish, könnte man komprimiert es weiter, aber ich weiß nicht empfehlen diese:
strlen(b)+1
gibt Sie, die Größe. Es geht nur schief, wenn der Anrufer hat sich etwas getan ungültig ersten, z.B. wenna
nicht auf ein ausreichend großer Puffer, aber das ist nicht unsere Schuld.a
Punkte um eine position vorb
innerhalb dieses Arrays können Sie nicht prüfen, ob diese.memmove
Frage stellt. Wenna
ist vorb
, und die Regionen, die wir über Pflege fürstrcpy
überschneiden, dann für einigel < strlen(b)+1
Sie Treffera + l == b
.a
unda + strlen(b)
für die Gleichstellung innerhalbb
. Dies wird funktionieren, aber langsam. 😉a + strlen(a) == b + strlen(b)
.strlen(a)
weil Sie nicht wissen, wie die Erinnerung ana
angeordnet ist.a
(Ziel) könnte so etwas wieabc\0def\0ghijklmnop\0
mitb
dem Hinweis auf dieghijklmnop
(könnte man so etwas vonstrtok()
). Die - strings ana
(abc
) und beib
(ghijklmnop
) nicht überlappen, aber die Erinnerung, dass Sie möchtenstrcpy( a, b )
zu kopieren (wenn dies erlaubt ist) würde überlappen.strcpy
, ist das Ergebnis undefiniert, aber falsch im Sinne von "nicht das, was wir erwartet und passiert seit Jahren").Könnten Sie wahrscheinlich verwenden Sie memmove (), wenn Sie erwarten, dass die Saiten zu überschneiden.
memmove()
erwartet Sie eine Größe in Zeichen und in C, "bytes" und Zeichen haben die gleiche Größe. "Der memmove-Funktion kopiert n Zeichen aus dem Objekt verweist s2 in das Objekt wies auf die von s1. C11 "7.24.2.2"Hinweis: Hier
b
ist die Adresse des Quell-string unda
ist die Adresse des Ziels.Mit
a > b
Sie würde nicht unbedingt überschneiden. Wenndann hast du eine überschneidung.
Jedoch, neben der Erkennung von überschneidungen zum Wohle interview
a > b
tun sollte, feinen fürstrcpy
. Die Idee ist diese:Wenn
b
platziert, weitere in den Speicher (b > a
), dann kannst du normal kopierenb
ina
. Teileb
überschrieben werden, aber Sie sind schon Vergangenheit, Teil.Wenn
a
platziert, weitere in den Speicher (a > b
), es bedeutet, dass möglicherweise durch das schreiben auf die erste Lage desa
Sie haben bereits überschrieben einem Standort inb
mit einem höheren index. In einem solchen Fall, sollten Sie kopieren Sie in die entgegengesetzte Richtung. Also statt der Kopie von index0
zustrlen(b)-1
Sie sollten kopieren vonstrlen(b)-1
zu0
.Wenn Sie verwirrt sind, wie das hilft, zeichnen Sie zwei sich überlappende arrays auf Papier und versuchen, Sie zu kopieren, sobald aus dem Beginn des Arrays und einmal am Ende. Versuchen Sie, diese mit den überlappenden arrays sowohl in den Fällen, in
a > b
unda < b
.Beachten, wenn
a == b
Sie müssen nicht wirklich alles kopieren, und Sie können einfach zurückgeben.Edit: ich bin mir nicht sicher, aber das Lesen der anderen Lösungen, wie es scheint, diese Antwort kann nicht vollständig portabel. Hüten Sie sich vor, dass.
a==b
Sie könnte auch einfach zurück 🙂strcpy
nimmt Zeiger auf nicht-volatile, also es gibt keine Voraussetzung, um wirklich berühren Sie den Speicher. Das heißt, es ist nicht Wert das hinzufügen von code zu optimieren, der absurde Fall.Können Sie beziehen sich auf eine Umsetzung von
memmove
ist, ist es ziemlich wie das, was ich sagte.(*) sollten Sie die cache-strlen(b) um die Leistung zu verbessern
Was es tut:
prüft, ob der
a+len
[Adresse a + extra-len bytes] ist innerhalb der Zeichenfolge, odera
[Adresse a] ist, innerhalb der Zeichenfolge, diese sind die einzigen Möglichkeiten für eine Zeichenfolge überschneiden.Wenn diese zwei strings überschneiden, dann, beim kopieren ist, dass man über die ursprüngliche
a
oderb
Zeiger.Unter der Annahme, dass strcpy( a, b ) etwa bedeutet, dass a <- b, d.h. der erste parameter ist das Ziel der Kopie, dann hast du nur überprüfen ob die Kopie Zeiger erreicht
b
's position.Brauchen Sie nur zu speichern die
b
ursprüngliche position, und beim kopieren, prüfen Sie noch nicht erreicht. Auch schreiben Sie nicht die nachgestellte null, wenn Sie erreicht haben, die position.Dieser Algorithmus hört einfach auf zu kopieren. Vielleicht möchten Sie etwas anderes tun, wie z.B. die Markierung der Fehler, oder fügen Sie ein Ende der Zeichenfolge markieren, um die Vorherige position (obwohl stillschweigendes fehlschlagen (wie der Algorithmus funktioniert im moment) nicht die beste option).
Hoffe, das hilft.
Wurde ich gebeten, dies in einem aktuellen interview. Wir don ' T haben, um 'erkennen' überlappen. Wir können schreiben
strcpy
in der Weise, dass sich überlagernde Adressen gesorgt. Der Schlüssel ist, um die Kopie aus dem Ende des Quell-Zeichenfolge anstelle von Anfang an.Hier ist ein quick-code.
EDIT: Dies funktioniert nur, wenn a < b. Für a > b, kopieren von Beginn an.
memcpy
Sie kopieren sollten von Anfang oder am Ende, basierend darauf, ob das Ziel, zu kopieren, hat niedrigere oder höhere Adresse als die Quelle beziehungsweise.src dest
im gesamten eher alsa b
. 3)strlen()
zurück gebensize_t
, aber dannsize_t i
verursacht Probleme mitwhile(i>=0)
test, die ist immer wahr.Sogar ohne die Verwendung der relationalen Zeiger-Vergleiche
memmove
, oder äquivalent, ist es möglich, code eine version vonstrcpy
die durchgeführt als einestrlen
undmemcpy
im nicht überlappenden Fall und als top-down-Kopie, die sich im überlappenden Fall. Der Schlüssel ist, zu nutzen, die Tatsache, dass, wenn das erste byte der Ziel gelesen wird, und dann durch null ersetzt, aufrufenstrlen
auf die Quelle und mit der Quelle Zeiger den Wert, der zurückgegeben wurde, wird der Ertrag einer legitimen Zeiger, die gleich zu Jahresbeginn das Ziel in der "lästige überschneidungen" der Fall. Wenn Quelle und Ziel verschiedene Objekte, die "Quelle plus strlen" - Zeiger kann sicher berechnet und beobachtet zu werden, ungleich der Ziel.Im Falle, dass das hinzufügen der string-Länge des Quell-pointer ergibt sich die Ziel-Zeiger, ersetzen die null-byte mit den früheren-Wert Lesen und aufrufen von strlen auf die Ziel-erlauben-code, um zu bestimmen, die Endung-Adresse des Quell-und Ziel-strings. Weiter ist die Länge der Quell-Zeichenkette gibt den Abstand zwischen den Zeigern. Wenn dieser Wert groß (vermutlich größer als 16 oder so), kann code effizient unterteilen Sie die "move" - Vorgang in einem top-down-Reihenfolge der memcpy-Operationen. Sonst wird der string kopiert werden kann mit einer top-down-Schleife von single-byte-Kopie-Operationen, oder mit einer Folge von "memcpy an der Quelle zu Puffern"/"Puffer memcpy zum Ziel" Operationen [wenn die pro-byte-Kosten für einen großen memcpy ist weniger als die Hälfte, dass von einer individuellen Charakter-Kopie-Schleife, mit einem ~256-byte-Puffer kann eine sinnvolle Optimierung].