Forum: PC-Programmierung Unterschied zwischen memcmp und strcmp – Wann was nehmen?


von Bartosz B. (bartosz)


Lesenswert?

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
int main(void)
6
{
7
  char buffer1[] = "Hallo";
8
  char buffer2[] = "Hallp";
9
  int n;
10
  n = memcmp(buffer1, buffer2, sizeof(buffer1)); // wird -1, weil das p in der ASCII-Tabelle später kommt als o
11
12
  //======================================================
13
14
  char buffer3[] = "Hallo";
15
  char buffer4[] = "Hallp";
16
  int x;
17
  x = strcmp(buffer3, buffer4);
18
  
19
  return 0;
20
}

Viele Grüße

Bartosz

von g457 (Gast)


Lesenswert?

> Warum gibt es sowohl memcmp als auch strcmp?

Zwei verschiedene Funktionen.

> Beide geben bei Gleichheit zweier Strings [..]

Nope. Nochmal ∗genau∗ lesen.

HTH

von A. S. (Gast)


Lesenswert?

Der fundamentale Unterschied wird auch daran sichtbar, dass strcmp den 
dritten Parameter nicht braucht. Da solltest Du forschen.

von Bartosz B. (bartosz)


Lesenswert?

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.

: Bearbeitet durch User
von Irgend W. (Firma: egal) (irgendwer)


Lesenswert?

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.
1
char buf1[16]; strcpy(buf1, "Hallo");
2
char buf2[16]; strcpy(buf2, "Hallo");
3
4
if (!memcmp(buf1, buf2, sizeof(buf))
5
{
6
  ...und wundern das man hier nicht immer ankommt
7
}

Der Unterschied steht schon im jeweils ersten Satz:
- https://en.cppreference.com/w/cpp/string/byte/memcmp
- https://en.cppreference.com/w/cpp/string/byte/strcmp

von Oliver S. (oliverso)


Lesenswert?

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

von Dirk B. (dirkb2)


Lesenswert?

Ersetze mal die "Hallp" in deinem Beispiel durch "Hallo Welt" (alle).
Was passiert jetzt?

von Bartosz B. (bartosz)


Lesenswert?

Dirk B. schrieb:
> Ersetze mal die "Hallp" in deinem Beispiel durch "Hallo Welt" (alle).
> Was passiert jetzt?

@Dirk habe ich gemacht
1
int main(void)
2
{
3
  char buffer1[] = "Hallo";
4
5
  char buffer2[] = "Hallo Welt";
6
7
  int n;
8
9
  n = memcmp(buffer1, buffer2, sizeof(buffer2)); //auch mit buffer1 getestet
10
11
  //======================================================
12
13
  char buffer3[] = "Hallo";
14
15
  char buffer4[] = "Hallo Welt";
16
17
  int x;
18
19
  x = strcmp(buffer3, buffer4);
20
21
  return 0;
22
}

Beide Ergebnisse werden **-1**, auch mit sizeof(buffer1)

von Dirk B. (dirkb2)


Lesenswert?

Bartosz B. schrieb:
> Beide Ergebnisse werden **-1**, auch mit sizeof(buffer1)

Ach ja, die '\0' ist ja noch beim sizeof dabei

von Bartosz B. (bartosz)


Lesenswert?

Ok, aber verstehe ich dich richtig, dass hier sizeof das Problem ist?

: Bearbeitet durch User
von Joachim B. (jar)


Lesenswert?

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!

von Bartosz B. (bartosz)


Lesenswert?

ok, ich danke euch!

von I_scho_wieda (Gast)


Lesenswert?

Aha, und strncmp wurde verboten?
Sehr seltsame Erklaerungen. ;)

von foobar (Gast)


Lesenswert?

> 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.

von Rolf M. (rmagnus)


Lesenswert?

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.

von (prx) A. K. (prx)


Lesenswert?

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.

: Bearbeitet durch User
von Mampf F. (mampf) Benutzerseite


Lesenswert?

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.

von Dirk B. (dirkb2)


Lesenswert?

Mampf F. schrieb:
> Man sollte aber eh strncmp benutzen.

Wenn ein C-String keine 0 enthält ist das ein Fehler, der mit strcmp 
eher auffallen kann.

Beitrag #6725187 wurde vom Autor gelöscht.
von mh (Gast)


Lesenswert?

(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?

von Mampf F. (mampf) Benutzerseite


Lesenswert?

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.

von mh (Gast)


Lesenswert?

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.

von (prx) A. K. (prx)


Lesenswert?

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.

von (prx) A. K. (prx)


Lesenswert?

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.

: Bearbeitet durch User
von Rolf M. (rmagnus)


Lesenswert?

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.

: Bearbeitet durch User
von Oliver S. (oliverso)


Lesenswert?

(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

: Bearbeitet durch User
von (prx) A. K. (prx)


Lesenswert?

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.

: Bearbeitet durch User
von Rolf M. (rmagnus)


Lesenswert?

(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.

von (prx) A. K. (prx)


Lesenswert?

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.

: Bearbeitet durch User
von Oliver S. (oliverso)


Lesenswert?

(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

von (prx) A. K. (prx)


Lesenswert?

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?

: Bearbeitet durch User
von Gerald K. (geku)


Lesenswert?

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)

: Bearbeitet durch User
von Rolf M. (rmagnus)


Lesenswert?

(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.

von (prx) A. K. (prx)


Lesenswert?

GCC, x86-64:
1
#include <string.h>
2
3
extern unsigned char s1[], s2[];
4
5
int f(void)
6
{
7
        return memcmp(s1, s2, 8);
8
}
9
10
11
int g(void)
12
{
13
        return 0 == memcmp(s1, s2, 8);
14
}
1
f:
2
        movl    $8, %edx
3
        leaq    s2(%rip), %rsi
4
        leaq    s1(%rip), %rdi
5
        jmp     memcmp@PLT
6
7
g:
8
        movq    s2(%rip), %rax
9
        cmpq    %rax, s1(%rip)
10
        sete    %al
11
        movzbl  %al, %eax
12
        ret

: Bearbeitet durch User
von Oliver S. (oliverso)


Lesenswert?

(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

: Bearbeitet durch User
von (prx) A. K. (prx)


Lesenswert?

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.

von (prx) A. K. (prx)


Lesenswert?

Funktion g() wie oben, aber memcmp mit 16 und 64 Bytes.
1
g:
2
  movq  8+s1(%rip), %rdx
3
  movq  s1(%rip), %rax
4
  xorq  8+s2(%rip), %rdx
5
  xorq  s2(%rip), %rax
6
  orq  %rax, %rdx
7
  sete  %al
8
  movzbl  %al, %eax
9
  ret
10
11
h:
12
  movq  8+s1(%rip), %rdx
13
  movq  s1(%rip), %rax
14
  xorq  8+s2(%rip), %rdx
15
  xorq  s2(%rip), %rax
16
  orq  %rax, %rdx
17
  jne  .L7
18
  movq  24+s1(%rip), %rdx
19
  movq  16+s1(%rip), %rax
20
  xorq  24+s2(%rip), %rdx
21
  xorq  16+s2(%rip), %rax
22
  orq  %rax, %rdx
23
  je  .L9
24
.L7:
25
  movl  $1, %eax
26
.L8:
27
  xorl  $1, %eax
28
  ret
29
.L9:
30
  movq  40+s1(%rip), %rdx
31
  movq  32+s1(%rip), %rax
32
  xorq  40+s2(%rip), %rdx
33
  xorq  32+s2(%rip), %rax
34
  orq  %rax, %rdx
35
  jne  .L7
36
  movq  56+s1(%rip), %rdx
37
  movq  48+s1(%rip), %rax
38
  xorq  56+s2(%rip), %rdx
39
  xorq  48+s2(%rip), %rax
40
  orq  %rax, %rdx
41
  jne  .L7
42
  xorl  %eax, %eax
43
  jmp  .L8

von Oliver S. (oliverso)


Lesenswert?

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

von foobar (Gast)


Lesenswert?

Das Wichtige oben waren die gleichgroszen Blöcke.  Selbst ohne 
Optimierung, also byteweisem Vergleich, geht es u.U. schief, z.B.:
1
  char buffer1[] = "Hallo";
2
  char buffer2[] = "Hallo\0Welt";
3
  strcmp(buffer1, buffer2)  // ok, true
4
  memcmp(buffer1, buffer2, sizeof(buffer2));  // undefined

Beitrag #6908529 wurde von einem Moderator gelöscht.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.