Warum gibt strcmp() 0 zurück, wenn seine Eingaben gleich sind?

Lesezeit: 8 Minuten

Benutzer-Avatar
Xenu

Wenn ich die C-String-Vergleichsfunktion wie folgt aufrufe:

strcmp("time","time")

Es gibt 0 zurück, was impliziert, dass die Zeichenfolgen nicht gleich sind.

Kann mir jemand sagen, warum C-Implementierungen dies zu tun scheinen? Ich würde denken, es würde einen Wert ungleich Null zurückgeben, wenn es gleich ist. Ich bin neugierig auf die Gründe, warum ich dieses Verhalten sehe.

  • Zeit != Geld direkt – Sie müssen zuerst eine Einheitenumrechnung vornehmen.

    – Adam Davis

    27. Februar 2009 um 16:28 Uhr

  • @remeador: Ich kann so viele dumme Scherzfragen stellen, wie ich will. Für die Interessierten lautete die ursprüngliche Frage: “Warum gibt strcmp(“time”,”money”) 0 statt Nicht-Null zurück?”.

    – Xenu

    6. März 2009 um 15:16 Uhr

  • Bearbeiten: Nicht-Null statt 0 zurückgeben.

    – Xenu

    6. März 2009 um 15:18 Uhr

Benutzer-Avatar
Benoît

strcmp gibt einen lexikalischen Unterschied (oder sollte ich es “kurzgeschlossener serieller Byte-Komparator” nennen? :-)) der beiden Zeichenfolgen zurück, die Sie als Parameter angegeben haben. 0 bedeutet, dass beide Strings gleich sind

Ein positiver Wert bedeutet, dass s1 in einem Wörterbuch nach s2 liegen würde.

Ein negativer Wert bedeutet, dass s1 in einem Wörterbuch vor s2 stehen würde.

Daher Ihr Nicht-Null-Wert beim Vergleich von “Zeit” und “Geld”, die offensichtlich unterschiedlich sind, obwohl man sagen würde, dass Zeit Geld ist! 🙂

  • strcmp() führt keinen lexikalischen Vergleich durch, es vergleicht lediglich den Wert jedes Zeichens, bis es einen Unterschied gibt oder beide Strings enden.

    – Ferruccio

    27. Februar 2009 um 16:35 Uhr

  • Ich könnte schwören, dass ich Dokumente gelesen habe, die besagten, dass strcmp() einen lexikalischen Vergleich durchführt, aber ein kurzes Googeln deutet darauf hin, dass Ferruccio richtig ist … könnte es sich irgendwann geändert haben?

    – rméador

    27. Februar 2009 um 16:38 Uhr

  • Hmmm, ich bin mir ziemlich sicher, dass es ein lexikalischer Vergleich ist! cplusplus.com/reference/clibrary/cstring/strcmp.html

    – Benoît

    27. Februar 2009 um 16:52 Uhr

  • Ich denke, es ändert sich mit der gegebenen Definition von “lexikalisch”. Ich habe auch gesehen, dass strcmp() als “lexikalischer” Komparator bezeichnet wird, obwohl es offensichtlich keine unterschiedlichen Lokalisierungen, Sortierungen usw. berücksichtigt. Vielleicht ist es besser, es einen “kurzgeschlossenen seriellen Byte-Komparator” zu nennen.

    – Aib

    27. Februar 2009 um 16:52 Uhr

  • Es verwendet einfach den ASCII-Wert jedes Zeichens zum Vergleich, der wie folgt geordnet ist: ABCDE…abcde. Ein lexikalischer Vergleich würde sie folgendermaßen ordnen: AaBbCcDdEe.

    – Ferruccio

    27. Februar 2009 um 17:00 Uhr

Benutzer-Avatar
Daniel Le Cheminant

Das Schöne an einer Implementierung wie dieser ist, dass man sagen kann

if(strcmp(<stringA>, <stringB>) > 0)   // Implies stringA > stringB
if(strcmp(<stringA>, <stringB>) == 0)  // Implies stringA == stringB
if(strcmp(<stringA>, <stringB>) < 0)   // Implies stringA < stringB
if(strcmp(<stringA>, <stringB>) >= 0)  // Implies stringA >= stringB
if(strcmp(<stringA>, <stringB>) <= 0)  // Implies stringA <= stringB
if(strcmp(<stringA>, <stringB>) != 0)  // Implies stringA != stringB

Beachten Sie, dass der Vergleich mit 0 genau mit dem Vergleich in der Implikation übereinstimmt.

  • Es gibt ein wunderbares/schreckliches Makro aus der comp.lang.c-FAQ, das fast genau dieses String-Verhalten implementiert. #define StrTest(str1, op, str2) (strcmp(str1, str2) op 0) Damit würden Sie schreiben if(StrTest(stringA, ==, stringB)) und Freunde. Ich bin hin- und hergerissen, ob es eine schreckliche Idee oder eine wunderbare Idee ist.

    – Chris Lutz

    2. September 2009 um 4:45 Uhr

Benutzer-Avatar
Johannes Schaub – litb

Es ist üblich, dass Funktionen Null für den allgemeinen – oder einmaligen – Fall und Nicht-Null für Sonderfälle zurückgeben. Nehmen Sie die main-Funktion, die üblicherweise bei Erfolg null und bei Misserfolg einen Wert ungleich null zurückgibt. Der genaue Wert ungleich Null gibt an, was schief gelaufen ist. Zum Beispiel: Speichermangel, keine Zugriffsrechte oder etwas anderes.

Wenn die Zeichenfolge in Ihrem Fall gleich ist, gibt es keinen Grund warum es ist gleich, außer dass die Zeichenfolgen dieselben Zeichen enthalten. Aber wenn sie ungleich sind, kann entweder der erste kleiner oder der zweite kleiner sein. Wenn es 1 für gleich, 0 für kleiner und 2 für größer zurückgibt, wäre das irgendwie seltsam, denke ich.

Sie können es sich auch in Form einer Subtraktion vorstellen:

return = s1 - s2

Wenn s1 “lexikografisch” kleiner ist, dann ergibt es einen negativen Wert.

Ein anderer Grund strcmp() gibt die Codes zurück, die es tut, damit es direkt in der Standardbibliotheksfunktion verwendet werden kann qsort()mit dem Sie ein Array von Zeichenfolgen sortieren können:

#include <string.h> // for strcmp()
#include <stdlib.h> // for qsort()
#include <stdio.h>

int sort_func(const void *a, const void *b)
{
    const char **s1 = (const char **)a;
    const char **s2 = (const char **)b;
    return strcmp(*s1, *s2);
}

int main(int argc, char **argv)
{
    int i;
    printf("Pre-sort:\n");
    for(i = 1; i < argc; i++)
        printf("Argument %i is %s\n", i, argv[i]);
    qsort((void *)(argv + 1), argc - 1, sizeof(char *), sort_func);
    printf("Post-sort:\n");
    for(i = 1; i < argc; i++)
        printf("Argument %i is %s\n", i, argv[i]);
    return 0;
}

Dieses kleine Beispielprogramm sortiert seine Argumente ASCIIbetisch (was manche lexikalisch nennen würden). Looki:

$ gcc -o sort sort.c
$ ./sort hi there little fella
Pre-sort:
Argument 1 is hi
Argument 2 is there
Argument 3 is little
Argument 4 is fella
Post-sort:
Argument 1 is fella
Argument 2 is hi
Argument 3 is little
Argument 4 is there

Wenn strcmp() ist zurückgekommen 1 (true) für gleiche Zeichenfolgen und 0 (falsch) für ungleiche wäre es unmöglich, es zu verwenden, um die zu erhalten Grad oder Richtung der Ungleichheit (dh wie unterschiedlich und welche größer ist) zwischen den beiden Zeichenfolgen, wodurch es unmöglich wird, sie als Sortierfunktion zu verwenden.

Ich weiß nicht, wie vertraut Sie mit C sind. Der obige Code verwendet einige der verwirrendsten Konzepte von C – Zeigerarithmetik, Zeigerumformung und Funktionszeiger -, wenn Sie also einen Teil dieses Codes nicht verstehen, machen Sie sich keine Sorgen, du kommst rechtzeitig an. Bis dahin haben Sie viele lustige Fragen, die Sie auf StackOverflow stellen können. 😉

Benutzer-Avatar
dmckee — Ex-Moderator-Kätzchen

Du scheinst zu wollen strcmp arbeiten wie ein (hypothetischer)

int isEqual(const char *, const char *)

Das würde natürlich für die „Null ist falsch“-Interpretation ganzzahliger Ergebnisse gelten, aber es würde die Logik des Sortierens verkomplizieren, da Sie, nachdem Sie festgestellt haben, dass die beiden Zeichenfolgen nicht gleich sind, immer noch lernen müssten, welche „früher“ gekommen ist “.

Außerdem vermute ich, dass eine gemeinsame Umsetzung so aussieht

int strcmp(const char *s1, const char *s2){
   const unsigned char *q1=s1, *q2=s2;
   while ((*q1 == *q2) && *q1){ 
      ++q1; ++q2; 
   };
   return (*q1 - *q2);
}

welches ist [edit: kinda] elegant auf K&R-Art. Der wichtige Punkt hier (der zunehmend durch den richtigen Code verdeckt wird (offensichtlich hätte ich ihn gut genug in Ruhe lassen sollen)) ist die Art und Weise, wie die return-Anweisung lautet:

   return (*q1 - *q2);

was die Ergebnisse des Vergleichs natürlich in Bezug auf die Zeichenwerte liefert.

  • Würde diese “übliche Implementierung” nicht aus dem Puffer gehen (nach der Nullterminierung?) Ich sehe nicht, wie sie jemals 0 zurückgeben würde …

    – Daniel Le Cheminant

    27. Februar 2009 um 17:05 Uhr

  • Ich denke, du musst die Schleife beenden, wenn du zu \0 kommst 🙂 Du musst auch auf Unterläufe in *s1 – *s2 achten, also s1=-127, s2=2 => oops 🙂

    – Johannes Schaub – litb

    27. Februar 2009 um 17:06 Uhr

  • Oh, Moment mal. no wird keinen Unterlauf verursachen, da beide zuerst in ein int konvertiert werden.

    – Johannes Schaub – litb

    27. Februar 2009 um 17:09 Uhr

  • @dmckee: Warum nicht einfach && *s1? :]

    – Daniel Le Cheminant

    27. Februar 2009 um 17:10 Uhr

  • Ich habe in c99 nachgesehen, es sagt, dass beide zuerst in unsigned char konvertiert werden, bevor sie subtrahiert werden. Es kann also nicht einmal einen Fall geben, in dem s1 = -127 ist 🙂 aber es ändert einiges: * a – * b könnte -2 werden mit char (*a=-1, *b=1) und +254 mit unsigned char.so glaube ich, dass dort ein Cast hinzugefügt werden sollte. irgendwie knifflig 🙂

    – Johannes Schaub – litb

    27. Februar 2009 um 17:16 Uhr

Benutzer-Avatar
David Thornley

Es gibt drei mögliche Ergebnisse: Zeichenfolge 1 kommt vor Zeichenfolge 2, Zeichenfolge 1 kommt nach Zeichenfolge 2, Zeichenfolge 1 ist dieselbe wie Zeichenfolge 2. Es ist wichtig, diese drei Ergebnisse getrennt zu halten; Eine Verwendung von strcmp() ist das Sortieren von Strings. Die Frage ist, wie Sie diesen drei Ergebnissen Werte zuweisen wollen und wie Sie die Dinge mehr oder weniger konsistent halten. Sie können sich auch die Parameter für qsort() und bsearch() ansehen, die Vergleichsfunktionen ähnlich wie strcmp() erfordern.

Wenn Sie eine String-Gleichheitsfunktion haben wollten, würde sie für gleiche Strings ungleich Null und für ungleiche Strings Null zurückgeben, um den C-Regeln für wahr und falsch zu entsprechen. Das bedeutet, dass es keine Möglichkeit gibt, zu unterscheiden, ob String 1 vor oder nach String 2 kommt. Es gibt mehrere wahre Werte für ein int oder jeden anderen C-Datentyp, den Sie benennen möchten, aber nur einen falschen.

Daher würde ein nützliches strcmp(), das bei String-Gleichheit true zurückgibt, viele Änderungen am Rest der Sprache erfordern, die einfach nicht passieren werden.

  • Würde diese “übliche Implementierung” nicht aus dem Puffer gehen (nach der Nullterminierung?) Ich sehe nicht, wie sie jemals 0 zurückgeben würde …

    – Daniel Le Cheminant

    27. Februar 2009 um 17:05 Uhr

  • Ich denke, du musst die Schleife beenden, wenn du zu \0 kommst 🙂 Du musst auch auf Unterläufe in *s1 – *s2 achten, also s1=-127, s2=2 => oops 🙂

    – Johannes Schaub – litb

    27. Februar 2009 um 17:06 Uhr

  • Oh, Moment mal. no wird keinen Unterlauf verursachen, da beide zuerst in ein int konvertiert werden.

    – Johannes Schaub – litb

    27. Februar 2009 um 17:09 Uhr

  • @dmckee: Warum nicht einfach && *s1? :]

    – Daniel Le Cheminant

    27. Februar 2009 um 17:10 Uhr

  • Ich habe in c99 nachgesehen, es sagt, dass beide zuerst in unsigned char konvertiert werden, bevor sie subtrahiert werden. Es kann also nicht einmal einen Fall geben, in dem s1 = -127 ist 🙂 aber es ändert einiges: * a – * b könnte -2 werden mit char (*a=-1, *b=1) und +254 mit unsigned char.so glaube ich, dass dort ein Cast hinzugefügt werden sollte. irgendwie knifflig 🙂

    – Johannes Schaub – litb

    27. Februar 2009 um 17:16 Uhr

Benutzer-Avatar
Philho

Ich denke, es ist einfach für Symmetrie: -1 wenn weniger, 0 wenn gleich, 1 wenn mehr.

1353530cookie-checkWarum gibt strcmp() 0 zurück, wenn seine Eingaben gleich sind?

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

Privacy policy