Comportement strcmp dans les systèmes 32 bits et 64 bits

Le code suivant se comporte différemment dans les systèmes d’exploitation 32 bits et 64 bits.

char *cat = "v,a"; if (strcmp(cat, ",") == 1) ... 

La condition ci-dessus est vraie en 32 bits mais fausse en 64 bits. Je me demande pourquoi c’est différent? Les systèmes d’exploitation 32 bits et 64 bits sont tous deux sous Linux (Fedora).

La fonction strcmp() n’est définie que pour renvoyer une valeur négative si l’argument 1 précède l’argument 2, zéro si elles sont identiques ou une valeur positive si l’argument 1 suit l’argument 2.

Il n’y a aucune garantie d’aucune sorte que la valeur renvoyée sera +1 ou -1 à tout moment. Tout test d’égalité fondé sur cette hypothèse est erroné. Il est concevable que les versions 32 bits et 64 bits de strcmp() renvoient des nombres différents pour une comparaison de chaînes donnée, mais tout test qui recherche +1 dans strcmp() est insortingnsèquement imparfait.

Votre code de comparaison devrait être l’un des suivants:

 if (strcmp(cat, ",") > 0) // cat > "," if (strcmp(cat, ",") == 0) // cat == "," if (strcmp(cat, ",") >= 0) // cat >= "," if (strcmp(cat, ",") <= 0) // cat <= "," if (strcmp(cat, ",") < 0) // cat < "," if (strcmp(cat, ",") != 0) // cat != "," 

Notez le thème commun - tous les tests comparent à 0. Vous verrez également les gens écrire:

 if (strcmp(cat, ",")) // != 0 if (!strcmp(cat, ",")) // == 0 

Personnellement, je préfère les comparaisons explicites avec zéro; Je traduis mentalement les raccourcis en long-métrage approprié (et je me sens mal à l'aise de le faire).


Notez que la spécification de strcmp() dit:

ISO / IEC 9899: 2011 §7.24.4.2 La fonction strcmp

¶3 La fonction strcmp renvoie un entier supérieur, égal ou inférieur à zéro, de sorte que la chaîne pointée par s1 est supérieure, égale ou inférieure à la chaîne pointée par s2 .

Il ne dit rien à propos de +1 ou -1 ; vous ne pouvez pas compter sur la magnitude du résultat, mais uniquement sur sa signature (ou sur la valeur zéro lorsque les chaînes sont égales).

Les fonctions standard ne présentent pas un comportement différent basé sur le “caractère” de votre système d’exploitation, sauf si vous faites quelque chose de stupide comme, par exemple, n’inclure pas le fichier d’en-tête approprié. Ils doivent présenter exactement le comportement spécifié dans la norme, à moins que vous ne respectiez les règles. Sinon, votre compilateur, même s’il est proche, ne sera pas un compilateur C.

Cependant, conformément à la norme, la valeur strcmp() par strcmp() est zéro, positive ou négative. Il n’est pas garanti qu’elle soit +/- 1 lorsqu’elle est différente de zéro.

Votre expression serait mieux écrite comme:

 strcmp (cat, ",") > 0 

L’utilisation incorrecte de strcmp (cat, ",") == 1 n’a rien à voir avec le système d’exploitation que vous possédez est 32 ou 64 bits, et tout avec le fait que vous avez mal compris la valeur de retour. De la norme ISO C11 (mon gras):

La fonction strcmp renvoie un entier supérieur, égal ou inférieur à zéro, de sorte que la chaîne pointée par s1 est supérieure, égale ou inférieure à la chaîne pointée par s2.

La sémantique garantie par strcmp() est bien expliquée ci-dessus dans la réponse de Jonathan .


Revenons à votre question initiale, à savoir

Q. Pourquoi le comportement de strcmp() diffère-t-il dans les systèmes 32 bits et 64 bits?

Réponse : strcmp() est implémenté dans la glibc, dans laquelle il existe différentes implémentations pour différentes architectures, toutes optimisées pour l’architecture correspondante.

  • strcmp () sur x86
  • strcmp () sur x86-64

Comme la spécification définit simplement que la valeur de retour est l’une des 3 possibilités (-ve, 0, + ve), les différentes implémentations sont libres de renvoyer n’importe quelle valeur tant que le signe indique le résultat de manière appropriée.

  • Sur certaines architectures (dans ce cas x86), il est plus rapide de comparer simplement chaque octet sans stocker le résultat. Par conséquent, il est plus rapide de simplement renvoyer – / + 1 en cas de non concordance.

    (Notez que vous pouvez utiliser subb au lieu de cmpb sur x86 pour obtenir la différence de magnitude des octets non correspondants. Mais cela nécessiterait un cycle d’horloge supplémentaire par octet. Cela signifierait une augmentation supplémentaire de 3% du temps total pris l’itération complète se déroule en moins de 30 cycles d’horloge.)

  • Sur d’autres architectures (dans ce cas x86-64), la différence entre les valeurs d’octet des caractères correspondants est déjà disponible en tant que sous-produit de la comparaison. Par conséquent, il est plus rapide de simplement le retourner plutôt que de le tester à nouveau et de renvoyer – / + 1.

Les deux sont des sorties parfaitement valides, car la fonction strcmp() n’a UNIQUEMENT le droit de renvoyer le résultat en utilisant le signe approprié et la magnitude est spécifique à l’architecture / à la mise en œuvre.