Unterschied zwischen memcmp und strcmp – Wann was nehmen?

OP #6724965
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
(Firma: egal) #6724980
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
OP #6725015
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)
Gast #6725104
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.
#6725175
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.
Beitrag #6725187 wurde von einem Moderator gelöscht.
Gast #6725226
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?
Persönliche Seite #6725232
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.
Gast #6725236
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.
#6725242
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.
#6725247
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.
#6725249
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
#6725253
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.
#6725286
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)
#6725297
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
#6725299
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
#6725304
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.
#6725311
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
#6725312
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
Gast #6725527
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.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren