Hallo, ich gehe gerade aus Übungszwecken alle Funktionen der string.h
durch und habe nun eine Frage: Warum gibt es sowohl memcmp als auch
strcmp?
Beide geben bei Gleichheit zweier Strings 0 zurück, sonst -1 oder 1 (je
nachdem, ob das erste ungleiche Zeichen früher oder später in der ASCII
kommt).
Hier habe ich die Infos her:
https://www.cplusplus.com/reference/cstring/
1
#include<stdio.h>
2
#include<string.h>
3
4
5
intmain(void)
6
{
7
charbuffer1[]="Hallo";
8
charbuffer2[]="Hallp";
9
intn;
10
n=memcmp(buffer1,buffer2,sizeof(buffer1));// wird -1, weil das p in der ASCII-Tabelle später kommt als o
> Warum gibt es sowohl memcmp als auch strcmp?
Zwei verschiedene Funktionen.
> Beide geben bei Gleichheit zweier Strings [..]
Nope. Nochmal ∗genau∗ lesen.
HTH
g457 schrieb:>> Warum gibt es sowohl memcmp als auch strcmp?>> Zwei verschiedene Funktionen.>>> Beide geben bei Gleichheit zweier Strings [..]>> Nope. Nochmal ∗genau∗ lesen.>> HTH
strcmp vergleicht 2 Strings, memcmp vergleicht 2 Speicherblöcke, also
Bytes. Letzten Endes werden aber in dem Beispiel auch 2 Strings
verglichen.
Bartosz B. schrieb:> strcmp vergleicht 2 Strings, memcmp vergleicht 2 Speicherblöcke, also> Bytes. Letzten Endes werden aber in dem Beispiel auch 2 Strings> verlichen.
Fast, aber nicht ganz.
Hier passt es nur eher zufällig weil in dem Beispiel "sizeof(x) =
strlen(x)+1" ist. Bei anderen konstelationen und gerade bei Verwendung
vom memcmp und sizeof kann das gründlich in die Hose gehen.
Bartosz B. schrieb:> Letzten Endes werden aber in dem Beispiel auch 2 Strings> verglichen.
Das ist richtig, aber der Unterschied liegt im offensichtlichen Detail…
Oliver
Bartosz B. schrieb:> Ok, aber verstehe ich dich richtig, dass hier sizeof das Problem ist?
nicht wirklich
Das Problem ist das strcmp keine Länge braucht weil ein "String" NULL
terminiert ist und sein SOLLTE also wird bei /0 der cmp abgebrochen,
memcmp weiss nicht wo Ende ist und erwartet eine Länge! und wenn lange
keine /0 kommt ist das Ergebnis Müll!
> char buffer1[] = "Hallo";> char buffer2[] = "Hallo Welt";> memcmp(buffer1, buffer2, sizeof(buffer2));
strcmp vergleicht byteweise und bricht bei dem ersten \0 ab. memcmp
vergleicht zwei gleichgrosze Blöcke in beliebigen Happen (z.B.
64-bit-weise). Im obigen Beispiel ist buffer1 kleiner als die dem
memcmp mitgeteilte Größe und er greift daher u.U. auf undefinierten
Speicher (hinter buffer1) zu. Das Ergebnis davon kann alles sein: ein
"korrektes" Ergebnis, ein Zufallsergebnis, aber auch ein SegFault.
foobar schrieb:> strcmp vergleicht byteweise und bricht bei dem ersten \0 ab.
Wobei es Gleichheit nur herausbekommt, wenn alles inklusive dem \0
gleich ist.
> memcmp vergleicht zwei gleichgrosze Blöcke in beliebigen Happen (z.B.> 64-bit-weise).
Was für "Happen"? Es vergleicht wie strcmp Byte für Byte.
Rolf M. schrieb:> Was für "Happen"? Es vergleicht wie strcmp Byte für Byte.
Implementierungssache. Wenn das gleiche rauskommt... Hingegen darf
strcmp nicht über \0 hinaus lesen, was der Optimierung Grenzen setzt.
Noch besser wird es, wenn 0==memcmp dasteht und der Compiler daraus
schliesst, dass auch bei falscher Byteorder größere Happen zum gleichen
Ergebnis führen.
Bartosz B. schrieb:> strcmp vergleicht 2 Strings, memcmp vergleicht 2 Speicherblöcke, also> Bytes. Letzten Endes werden aber in dem Beispiel auch 2 Strings> verglichen.
Ergänzend: strcmp verlässt sich auf den Zero-Terminator von C-Strings,
während man bei memcmp explizit die Länge angeben muss.
Man sollte aber eh strncmp benutzen.
(prx) A. K. schrieb:> Rolf M. schrieb:>> Was für "Happen"? Es vergleicht wie strcmp Byte für Byte.>> Implementierungssache. Wenn das gleiche rauskommt... Hingegen darf> strcmp nicht über \0 hinaus lesen, was der Optimierung Grenzen setzt.
Gibt es eine explizite Regel, die dem Compiler verbietet mehr als 1 zu
lesen und zu vergleichen? Mit "As-If" sehe ich keinen Grund für ein
solches Verbote.
> Noch besser wird es, wenn 0==memcmp dasteht und der Compiler daraus> schliesst, dass auch bei falscher Byteorder größere Happen zum gleichen> Ergebnis führen.
Was genau beschreibst du hier? memcmp gibt einen anderen Wert als 0
zurück, wenn die zu vergleichenden Objekte nicht bytewise gleich sind?
mh schrieb:>> Noch besser wird es, wenn 0==memcmp dasteht und der Compiler daraus>> schliesst, dass auch bei falscher Byteorder größere Happen zum gleichen>> Ergebnis führen.> Was genau beschreibst du hier? memcmp gibt einen anderen Wert als 0> zurück, wenn die zu vergleichenden Objekte nicht bytewise gleich sind?
Ja genau, wobei nicht spezifiziert ist, was genau.
Wenn man größer/kleiner-Vergleiche machen möchte, muss man memcmp selbst
implementieren.
Mampf F. schrieb:> mh schrieb:>>> Noch besser wird es, wenn 0==memcmp dasteht und der Compiler daraus>>> schliesst, dass auch bei falscher Byteorder größere Happen zum gleichen>>> Ergebnis führen.>> Was genau beschreibst du hier? memcmp gibt einen anderen Wert als 0>> zurück, wenn die zu vergleichenden Objekte nicht bytewise gleich sind?>> Ja genau, wobei nicht spezifiziert ist, was genau.
Upss, da ist ein "nicht" zu viel. Es sollte "wenn sie byteweise gleich
sind" heißen.
mh schrieb:> Gibt es eine explizite Regel, die dem Compiler verbietet mehr als 1 zu> lesen und zu vergleichen?
Strings über \0 hinaus zu lesen kann zum Programmabbruch führen, wenn
dort überhaupt kein zulässiger Speicher mehr existiert.
Prozessor-Microcode kann ggf damit umgehen, nicht aber Anwendercode.
mh schrieb:>> Noch besser wird es, wenn 0==memcmp dasteht und der Compiler daraus>> schliesst, dass auch bei falscher Byteorder größere Happen zum gleichen>> Ergebnis führen.> Was genau beschreibst du hier? memcmp gibt einen anderen Wert als 0> zurück, wenn die zu vergleichenden Objekte nicht bytewise gleich sind?
Gleich ist gleich, ob byte- oder wortweise. Bei grösser/kleiner sieht
das anders aus.
POSIX: "The memcmp() function shall return an integer greater than,
equal to, or less than 0, if the object pointed to by s1 is greater
than, equal to, or less than the object pointed to by s2, respectively."
Und zwar bezogen auf byteweisen Vergleich definiert.
Wenn du breitere Operationen als einzelne Bytes verwendest, funktioniert
das Vorzeichen (ohne explizitem Byte-Swap) nur bei einer big-endian
Architektur.
Vergleichst du das Resulat hingegen auf ==0 und der Compiler erzeugt
memcmp Code selbst, statt Libcode einzubinden, entfällt die Frage nach
dem Vorzeichen und die Bytereihenfolge wird irrelevant.
mh schrieb:> (prx) A. K. schrieb:>> Rolf M. schrieb:>>> Was für "Happen"? Es vergleicht wie strcmp Byte für Byte.>>>> Implementierungssache. Wenn das gleiche rauskommt... Hingegen darf>> strcmp nicht über \0 hinaus lesen, was der Optimierung Grenzen setzt.> Gibt es eine explizite Regel, die dem Compiler verbietet mehr als 1 zu> lesen und zu vergleichen?
Nein. Genauso wenig gibt es aber eine Regel, die besagt, dass das immer
blockweise geschehen würde oder dass das bei strcmp niemals passieren
dürfte. Aber gerade das wurde oben als Unterschied zwischen den beiden
Funktionen genannt. Auf Ebene der Sprachdefinition gibt es einen solchen
Unterschied nicht, auch wenn eine libc bzw. ein Compiler das so
implementieren mag.
> Mit "As-If" sehe ich keinen Grund für ein solches Verbote.
Genau. Die abstrakte Maschine vergleicht byteweise. Auf Grund der
as-if-Regel darf der Compiler davon abweichen, aber das ist ein
Implementierungsdetail. Wesentlich aber ist, dass memcmp einen Wert
zurückgibt, der im Falle von Ungleichheit anzeigt, ob das erste sich
unterscheidende Byte (und zwar nur das eine Byte) des ersten Blocks
einen größeren oder einen kleineren Wert als das des zweiten hat.
(prx) A. K. schrieb:> Wenn du breitere Operationen als einzelne Bytes verwendest, funktioniert> das Vorzeichen (ohne explizitem Byte-Swap) nur bei einer big-endian> Architektur.
Was meinst du mit „funktionieren“? Da Byte für Byte verglichen wird, in
Form eines unsigned char, ist die Endianess doch völlig egal. Der
zurückgelieferte Wert sagt nur etwas über das erste ungleiche Byte aus,
nicht mehr und nicht weniger. Das Ergebnis hängt zwar von der Endianess
ab, und ist nicht portabel, aber „funktionieren“ wird das immer.
Oliver
Oliver S. schrieb:> Was meinst du mit „funktionieren“? Da Byte für Byte verglichen wird, in> Form eines unsigned char, ist die Endianess doch völlig egal.Wenn du einzelne Bytes vergleichst, eines nach dem anderen. Ich bezog
mich auf eine Implementierung von memcmp, die bei passender Länge und
evtl Alignment beispielsweise 64-Bit Worte vergleicht, was schon ein
wenig schneller sein kann. Dann ist die Endianess nicht mehr egal.
Bei strcmp und nicht sowieso schon bekannter Länge der Strings geht das
nicht, weil bei \0 definitiv Ende sein muss. Hingegen vergleicht memcmp
Speicherblöcke bekannter Länge.
(prx) A. K. schrieb:> Dann ist die Endianess nicht mehr egal.
Warum? Entweder sind die Worte gleich, dann ist das unabhängig von der
Endianness, oder es gibt einen Unterschied, dann muss aber sowieso das
Byte, das sich unterscheidet, einzeln rausgefummelt werden für den
Returnwert.
Rolf M. schrieb:> dann muss aber sowieso das> Byte, das sich unterscheidet, einzeln rausgefummelt werden für den> Returnwert.
Bei big-endian stimmts auch so, so lange man nicht eine nackte Differenz
ausserhalb des int Wertebereichs zurück gibt, sondern ggf auf 1/-1
reduziert.
(prx) A. K. schrieb:> Ich bezog> mich auf eine Implementierung von memcmp, die bei passender Länge und> evtl Alignment beispielsweise 64-Bit Worte vergleicht, was schon ein> wenig schneller sein kann. Dann ist die Endianess nicht mehr egal.
Dann ist es aber kein Standard-konformes memcmp mehr, sondern irgend
eine selbst definierte Funktion.
Oliver
Oliver S. schrieb:> Dann ist es aber kein Standard-konformes memcmp mehr, sondern irgend> eine selbst definierte Funktion.
Worin besteht die Abweichung von der Definition im Standard?
So wie der Name schon sagt:
strcmp für den Vergleich, von Strings Länge wird über 0 Char ermittelt
int strcmp(char *str1, char *str2);
memcmp für den Vergleich, von Speicherinhalte über zwei Adressen und
Länge
int memcmp(const void *str1, const void *str2, size_t n)
(prx) A. K. schrieb:> Bei big-endian stimmts auch so, so lange man nicht eine nackte Differenz> ausserhalb des int Wertebereichs zurück gibt, sondern ggf auf 1/-1> reduziert.
Hmm, stimmt, daran hatte ich nicht gedacht.
(prx) A. K. schrieb:> Worin besteht die Abweichung von der Definition im Standard?
Keine Ahnung, ich kenne ja deine Implementierung nicht.
memcmp vergleicht keine C-Datentypen, sondern per Definition Byte für
Byte von zwei unsigned char arrays in aufsteigender Reihenfolge, mehr
nicht. Vergleicht man damit irgend etwas anders als unsigned char
arrays, so ist das Ergebnis bezgl. größer/kleiner natürlich plattform-
und implemetierungsabhängig.
„Funktionieren“ im Sinne von funktionieren tut das aber immer.
Oliver
Oliver S. schrieb:> memcmp vergleicht keine C-Datentypen, sondern per Definition Byte für> Byte von zwei unsigned char arrays in aufsteigender Reihenfolge, mehr> nicht.
In C gilt die "as if rule". Es reicht aus, wenn sich eine
Implementierung entsprechend dem Standard verhält. Wenn der Standard
also einen byteweisen Vergleich definiert, muss die Implementierung
nicht byteweise erfolgen. Es muss nur das gleiche Ergebnis herauskommen.
Da sind wir uns ja einig. Natürlich kann und wird da intern der Compiler
größere Blöcken als nur Bytes vergleichen. Das zurückgelieferte Ergebnis
muß aber dem entsprechen, was sich bei einem rein byteweisen Vergleich
ergeben hätte.
(prx) A. K. schrieb:> Wenn du breitere Operationen als einzelne Bytes verwendest, funktioniert> das Vorzeichen (ohne explizitem Byte-Swap) nur bei einer big-endian> Architektur.
Darauf muß man bei der Implementierung von memcmp Rücksicht nehmen, aber
nicht bei der Anwendung.
Die ganze Diskussion hier dreht sich aber um die Anwendung, nicht die
Implementierung.
Oliver