strcmp(3)

Fonction de la bibliothèque C

Compare deux chaînes octet par octet et renvoie un entier négatif, nul ou positif. Pourquoi == ne compare pas des chaînes, et pourquoi seul le signe compte.

Synopsis
#include <string.h>
int strcmp(const char *s1, const char *s2);

Description

strcmp avance dans s1 et s2 en même temps et s'arrête au premier octet qui diffère, ou à l'octet nul s'ils sont identiques jusqu'au bout. La norme précise que les deux octets comparés sont lus comme des unsigned char. La comparaison suit donc les codes des octets, pas l'ordre alphabétique d'une langue : la locale n'est pas prise en compte, c'est le rôle de strcoll.

Valeur de retour

Un entier négatif si s1 vient avant s2, zéro si les deux chaînes sont égales, un entier positif si s1 vient après. Seul le signe est garanti. La valeur exacte dépend de l'implémentation, comme le montre l'exemple : la même comparaison donne -32 avec la glibc de cette machine ARM64, -1 avec musl.

Pièges

== compare des adresses. if (saisie == "oui") demande si saisie et la chaîne littérale sont rangées au même endroit, ce qui n'arrive presque jamais. GCC le signale avec -Wall : comparison with string literal results in unspecified behavior. Pour comparer le contenu, c'est strcmp(saisie, "oui") == 0.

if (strcmp(a, b)) est vrai quand les chaînes sont différentes, puisque l'égalité renvoie 0. Écris toujours la comparaison en entier, == 0 ou != 0, le code se relit mieux.

Ne teste jamais == 1 ou == -1 : ça marche avec une bibliothèque et pas avec une autre. Compare à zéro, < 0 ou > 0.

L'ordre des codes surprend. Les majuscules (65 à 90 en ASCII) passent avant les minuscules (97 à 122), donc "Zoe" vient avant "alice". En UTF-8, une lettre accentuée commence par un octet supérieur à 127, donc "été" vient après "zoo".

Passer NULL à strcmp est un comportement indéfini, en général un segmentation fault.

Exemple

Le programme compare ses arguments deux par deux. Ils arrivent à l'exécution, donc c'est bien la bibliothèque C qui calcule le résultat : avec deux chaînes littérales, GCC peut faire la comparaison lui-même à la compilation.

compare.c
#include <stdio.h>#include <string.h>
int main(int argc, char **argv){    for (int i = 1; i + 1 < argc; i += 2)        printf("strcmp(\"%s\", \"%s\") = %d\n", argv[i], argv[i + 1], strcmp(argv[i], argv[i + 1]));    return 0;}

Avec la glibc (Debian 13) :

Terminal
$ gcc -Wall -Wextra compare.c -o compare$ ./compare abc abc abc abd abd abc ab abc Zoe alice été zoostrcmp("abc", "abc") = 0strcmp("abc", "abd") = -32strcmp("abd", "abc") = 32strcmp("ab", "abc") = -198strcmp("Zoe", "alice") = -7strcmp("été", "zoo") = 73

Avec musl, dans l'environnement des exercices Grademe :

Terminal
$ gcc -Wall -Wextra compare.c -o compare$ ./compare abc abc abc abd abd abc ab abc Zoe alice été zoostrcmp("abc", "abc") = 0strcmp("abc", "abd") = -1strcmp("abd", "abc") = 1strcmp("ab", "abc") = -99strcmp("Zoe", "alice") = -7strcmp("été", "zoo") = 73

Les signes sont identiques ligne par ligne, les valeurs non. musl renvoie la différence des deux octets ('c' - 'd' vaut -1, '\0' - 'c' vaut -99), la version de la glibc pour cette machine ARM64 renvoie autre chose. Les deux respectent la norme.

C

strcmp

Écris gm_strcmp, qui parcourt les deux chaînes jusqu'au premier octet différent et renvoie une valeur dont le signe donne leur ordre.

Ouvrir l'exercice

À toi d'écrire le code.