7
votes

MisMatch signé / non signé lors de la comparaison de deux valeurs non signées à l'aide d'un opérateur conditionnel

J'ai le code C suivant: xxx

lorsque je le compile (avec Microsoft cl , de MS SDK 7, -w3 Niveau d'avertissement), la deuxième comparaison émet un avertissement: C4018, instabilité signée / non signée. La première comparaison émet aucun avertissement.

J'ai vérifié MS Docs sur l'opérateur conditionnel et ils indiquent que si les deux opérandes sont de même type, le résultat sera du même type. Il devrait donc fonctionner comme première comparaison. Est-ce que je manque quelque chose?

upd: Testé avec gcc -wall -wextra -pedantique et n'a aucun avertissement.


4 commentaires

Tous les avertissements de compilation sont-ils allumés dans vos options de compilateur?


Oui, un bug / une fonctionnalité non confirmée. Je suis allé dessus et je me suis lancé, si VC se plaint et ne le pense pas plus.


@CowBoydan Oui, -W3 . Si je spécifie un niveau d'avertissement, il n'y a aucun avertissement.


GCC ne produit aucun avertissement.


3 Réponses :


7
votes

Ceci est probablement dû aux règles de conversion arithmétiques: tout d'abord, tout type de type de conversion entier inférieur à int (par exemple, non signé char ) favorisera à int ou non signé INT .

Si le résultat sera int ou non signé INT ne dépend pas de la signature du type d'origine, mais sa plage: int est utilisé même pour des types non signés tant que toutes les valeurs peuvent être représentées, ce qui est le cas pour sans signé Char sur les architectures traditionnelles.

Deuxièmement, comme les deux opérandes se retrouvent avec le même rang de conversion, mais un non signé, l'autre opérande sera également converti en un type non signé.

sémantiquement, vos expressions lues xxx

et xxx

Le compilateur est apparemment assez intelligent pour noter que le premier cas ne peut pas causer de problèmes, mais échoue à le second.

Le commentaire de Steve Jessop explique bien comment cela pourrait arriver:

J'imagine que dans le premier cas, le compilateur pense: "J'ai un opérateur de comparaison dont les types d'opérande sont non signé INT et non signé Char . Pas besoin d'un avertissement , appliquons maintenant une promotion suivie d'une conversion habituelle ».

Dans le second cas, il pense: "J'ai un opérateur de comparaison dont les types d'opérande sont non signé int et int (que j'ai dérivé le type de l'expression conditionnelle sur le RHS). Meilleur avertissant de cela! ".


6 commentaires

Strictement parlant, le 2e cas est équivalent à A <(INT) (INT) (int) (B? (Int) B: (int) b: (int) c) en raison d'une règle étrange spéciale dans l'opérateur conditionnel. 6.5.15 / 3 Si les deuxième et troisième opérandes ont des types arithmétiques, le type de résultat qui serait déterminé par les conversions arithmétiques habituelles, ont-ils été appliqués à ces deux opérandes, est le type de résultat. Bien sûr, cela n'a pas d'importance pour le résultat dans ce cas particulier.


@LUNDIN: En fait, j'ai réfléchi à ce que cette conversion (ainsi que la promotion du premier B ) mais a décidé de cela pour des raisons pédagogiques; Votre réponse est en effet plus complète, donc +1 de moi


J'imagine que dans le premier cas, le compilateur pense: "J'ai un opérande de comparaison dont les types sont non signé int et non signé char . Pas besoin d'un avertissement, maintenant appliquons maintenant Promotion suivie d'une conversion habituelle ». Dans le second cas, il pense: "J'ai un opérande de comparaison dont les types sont non signés INT et int (que j'ai dérivé le type de l'expression conditionnelle sur le RHS). Meilleur avertir à ce sujet! ".


@Stevejessop: J'espère que ça ne vous dérange pas de me voler votre belle explication


@Stevejessop En fait, cela pourrait être, car le compilateur n'est pas obligé de faire toutes les promotions implicites si elle peut dire qu'elles n'affecteront pas le résultat de l'expression. Supposons que cela traduit ce code directement dans "Afficher A et B dans des registres CPU 32 bits, puis comparez".


@Christoph: Ne vous dérange pas du tout, bien que j'ai typée "Opérande" au lieu de "opérateur" deux fois! Dis peut-être "un opérateur de comparaison dont les types d'opérande sont ..."



3
votes

si (a est égal au pseudo si (non signé int . .

Chaque fois qu'un type de caractère est utilisé dans une expression, les règles de promotion entier en C convertit implicitement à un int . Après cela est fait, vous avez

si (non signé int .

Chaque fois que deux entiers du même rang mais que la signature différente est utilisée dans une expression, la signature est implicitement convertie en non signé. Ceci est déterminé par les conversions arithmétiques habituelles , aka équilibre .

Donc, votre première expression est convertie en

si (non signé INT

Avant que tout ce qui soit fait.


dans la deuxième expression que nous avons si (a <(b: b: c)) qui équivaut à Pseudo

si (non signé INT <(non signé Char? non signé Char: Unsigné Char)) .

Les promotions entier sont effectuées sur tous les caractères, il est donc implicitement converti en

si (non signé INT <(int? int: int)) .

Ensuite, une règle étrange et obscurée de l'opérateur conditionnel dicte que le 2e et 3ème opérateur de l'opérateur?: L'opérateur doit être équilibré avec les conversions arithmétiques habituelles. Dans ce cas, ils sont déjà du même type, alors rien ne se passe. Nous finissons avec

si (non signé int

L'équilibrage se produit à nouveau, le résultat sera évalué comme

si (non signé INT


Quand je le compile avec Microsoft

Lorsque vous compilez avec Microsoft, tous les paris sont éteints, leur compilateur est très pauvre pour suivre la norme. Attendez-vous à des avertissements bizarres et illogiques.


0 commentaires

1
votes

Les règles sont différentes entre C et C ++. La norme appropriée peut être difficile à juger lors de la compilation C avec MSVC, mais heureusement dans ce cas, C89 et C99 sont les mêmes.

en C89 3.3.15:

Si les deuxième et troisième opérandes ont un type arithmétique, l'habitude Les conversions arithmétiques sont effectuées pour les amener à un type commun et le résultat a ce type

en C99 6.5.15 / 5:

Si les deuxième et troisième opérandes ont un type arithmétique, le résultat type qui serait déterminé par les conversions arithmétiques habituelles, ont-ils été appliqués à ces deux opérandes, est le type de résultat.

en C ++ 03 5.16 / 4:

Si les deuxième et troisième opérandes sont des lvalues ​​et ont le même type, Le résultat est de ce type et est un lvalue

Ainsi, lorsque vous, disons: "Si les deux opérandes sont de même type, le résultat sera du même type, il devrait donc fonctionner comme la première comparaison", qui ne s'applique qu'à C ++, non à C. en C Type du RHS de cette comparaison est INT . En C ++, le RHS serait une lvalue de type non signé Char comme prévu.


4 commentaires

Les promotions entières ne s'appliqueraient-elles pas même en C ++ après la conversion de Lvalue-to-RValue?


@Cristoph: Oui. En C ++, les types d'opérandes de << / code> seraient non signé INT et non signé Char . alors Promotion et conversion s'appliquent aux opérandes. En C, les types des opérandes de << / code> sont non signé int et int , puis la promotion et la conversion s'appliquent aux opérandes.


Ma "réponse" devrait probablement être un commentaire, en fait. Il parle "si les deux opérandes sont de même type, le résultat sera du même type ... je manque quelque chose?" (tiré de la DOCS de MS), plutôt que la question actuelle impliquée "Pourquoi est-ce que je reçois cet avertissement?". Mais il n'ya aucun moyen de répondre à ces citations standard dans un commentaire et, étant donné que la seule question que vous demandez est ", manque-t-il quelque chose?" alors la réponse à votre question est "oui" ;-)


@Stevejessop vous êtes absolument juste; Si je le compile en mode C ++ (avec -TP ) je n'arrête pas d'avertissement ici. Merci!