2013-10-26 14 views
5

Ich schaute Public-Domain-Implementierungen auf wikibooks.org. Es implementiert memmove() wie folgt explizit, dass es "nicht vollständig portabel" ist! Ich habe mich gefragt, warum:In Bezug auf die Implementierung von memmove

  1. Klammern wurden in der ersten Zeile des Codes platziert und
  2. der Code nicht vollständig tragbar.

Der Code ist wie folgt:

void *(memmove)(void *s1, const void *s2, size_t n) 
{ 
    char *p1 = s1; 
    const char *p2 = s2; 

    if (p2 < p1 && p1 < p2 + n) { 
     /* do a descending copy */ 
     p2 += n; 
     p1 += n; 
     while (n-- != 0) 
      *--p1 = *--p2; 
    } else 
     while (n-- != 0) 
      *p1++ = *p2++; 

    return s1; 
} 
+3

Gute Frage, lesen [Was bedeuten die Klammern um einen Funktionsnamen?] (Http://stackoverflow.com/questions/13600790/what-do-the-parentheses-around-a-function-name-mean/ 13600837 # 13600837) –

+0

Vielen Dank, es war nützlich! – user926918

Antwort

9

Die Spezifikation der Funktion memmove() ist, dass es Quelle und Ziel überlappend verarbeiten kann, aber die Spezifikation sagt nicht, dass memmove() mit Zeigern auf die gerufen werden muss, gleicher Speicherblock ("Objekt" im Sprachgebrauch des Standards).

Wenn p1 und p2 Zeiger auf verschiedene Speicherblöcke sind, die Bedingung p2 < p1 ist undefinierten Verhalten. Der C99-Standard sagt (6.5.8: 5):

Wenn zwei Zeiger verglichen werden, das Ergebnis auf den relativen Stellen in dem Adressraum der Objekte darauf abhängt. Wenn zwei Zeiger auf Objekt oder unvollständige Typen beide auf das gleiche Objekt zeigen, oder beide hinter dem letzten Element desselben Array-Objekts liegen, dann vergleichen sie gleich. Wenn die Objekte, auf die verwiesen wird, Mitglieder desselben Aggregatobjekts sind, vergleichen Zeiger auf später deklarierte Strukturelemente größer als Zeiger auf zuvor in der Struktur deklarierte Elemente und Zeiger auf Arrayelemente mit größeren tiefgestellten Werten vergleichen größer als Zeiger auf Elemente des gleichen Arrays mit tieferen tiefgestellten Werten. Alle Zeiger auf Mitglieder desselben Union-Objekts vergleichen gleich. Wenn der Ausdruck P auf ein Element eines Arrays Objekt zeigt und der Ausdruck Q auf das letzte Element desselben Array-Objekts zeigt, vergleicht der Zeigerausdruck Q + 1 größer als P. In allen anderen Fällen ist das Verhalten nicht definiert .

Ich weiß nicht, ob das ist, worauf sich die Erklärung bezieht, aber es ist eine eindeutige Quelle der Nichttragbarkeit. Eine andere Implementierung könnte (uintptr_t)p2 < (uintptr_t)p1 verwenden. Dann ist der Vergleich < ein Vergleich zwischen ganzen Zahlen. Die Konvertierung in.liefert implementierungsdefinierte Ergebnisse. Der Typ uintptr_t wurde in C99 eingeführt und ist ein vorzeichenloser Integertyp, der garantiert die Darstellung eines Zeigers enthält.

Eine vollständig portierbare Implementierung von memmove() könnte einen dritten Puffer für eine Zwischenkopie verwenden, oder use == comparison (die angegebene Ergebnisse in dem Kontext angibt, in dem sie verwendet werden müsste).

5
  1. Die Erklärung der Klammern ist hier: What do the parentheses around a function name mean?

  2. es wegen der p2 < p1 und p1 < p2 + n Vergleiche nicht tragbar ist.Der C-Standard definiert nur das Verhalten von Zeigervergleichen, wenn die beiden Zeiger auf dasselbe Objekt zeigen. Dieser Code hängt davon ab, dass sie auch dann ordnungsgemäß funktionieren, wenn Sie zwischen verschiedenen Objekten kopieren.

In einem praktischen Sinne ist der Code in Ordnung. Wenn die Zeiger nicht auf dasselbe Objekt zeigen, spielt es keine Rolle, ob die Kopie aufsteigend oder absteigend erstellt wird, daher sind die Ergebnisse der Vergleiche irrelevant. Alles, was zählt, ist, dass der Code nichts wirklich Schreckliches tut, wie den Prozess zum Absturz bringen oder die nuklearen Startcodes senden. Der C-Standard verbietet dies nicht, aber es ist unwahrscheinlich, dass es sich um eine realistische Implementierung handelt. Die meisten Implementierungen vergleichen einfach die unformatierten Adressen, und irgendwelche seltsamen Implementierungen geben nur einen unvorhersehbaren Wert zurück, haben aber keine Nebenwirkungen.

+1

Der Code ist für eine moderne Plattform mit einem flachen Adressraum in Ordnung. Steve Jessop hat eine gute Erklärung für C++ 's st :: less' in seiner Antwort auf eine alte Frage von mir (die ich in meiner Antwort auf diese Frage verlinkt habe). –

Verwandte Themen