8
votes

L'opérateur conditionnel devrait-il évaluer tous les arguments?

Lorsque vous écrivez ceci:

line 4: warning C4723: potential divide by 0


5 commentaires

Prenez soin de la comparaison des "doubles" arguments sur l'égalité. Mauvais magie arrive là-bas ...


Non ça ne le fait pas. Processus parfaitement bien défini. En particulier, 0,0 == -0.0 . Par conséquent, pour tous les ensembles de valeurs pour lesquels 1./arg est défini, nous savons que arg! = 0.0 .


@Msalters: Mais en raison des erreurs d'arrondi, arg, peut ne pas être 0,0 (ou -0,0.0.0.0.0.0.0.0 à cette affaire) lorsque vous vous attendiez.


Ce n'est pas un bug en ce qui concerne la conformité des normes. Mais cela ne signifie pas que vous ne pouvez pas soumettre un rapport de bogue à Connect.Microsoft.com. Vous avez raison que dans ce cas, il pourrait être raisonnable de s'attendre à ce que le compilateur détermine que la division par zéro ne peut jamais arriver. Laissez Microsoft savoir alors.


@jalf: True, il peut ne pas être zéro à cause de l'arrondi. Mais dans ce cas, il n'y a pas de fracture par zéro. Les deux états suivants sont entièrement équivalents et interchangeables: x == 0 et 1./x est une division par zéro (sous IEE754 et toutes les autres implémentations que je connaisse)


8 Réponses :


11
votes

Le compilateur n'est pas en mesure d'analyser statiquement tous les chemins de code et de prendre en compte toutes les possibilités tout le temps. Théoriquement, une analyse complète d'un comportement de programme Juste en examinant son code source peut fournir une solution à la halte de problème, ce qui est indécitable. Les compilateurs ont un ensemble limité de règles d'analyse statique pour détecter les règles. La norme C ++ ne nécessite pas que le compilateur émet un tel type d'avertissements. Ce n'est pas un bug. C'est plus comme une fonctionnalité inexistante.


5 commentaires

Tu as raison. Il ne peut jamais être un bug du point de vue standard C ++, car cela concerne les avertissements. Cependant, si ce «défaut» me force à réécrire le code parfaitement valide (ou à l'entourer avec #pragma s), à partir d'un point de vue de «programme informatique», c'est un bug.


@xtofl: le mot clé dans l'avertissement est "potentiel"


D'accord, mais la douleur essentielle est que nous essayons de traiter des avertissements comme des erreurs et ne peuvent pas le faire avec des avertissements «faux positifs».


@xtofl: Vous utilisez un outil. Si cela donne des avertissements inutiles, vous devrez les ignorer. C'est peut-être un "bogue" pour vous, mais c'est l'outil que vous utilisez et vous ne pouvez pas la modifier.


@xtofl: Vous pourriez avoir l'avertissement #pragma (désactiver: 4723)



7
votes

Non, l'opérateur conditionnel n'évalue pas les deux arguments. Cependant, une division potentielle à zéro, si un compilateur peut détecter une telle chose, est généralement rapporté. Il n'est pas inauguré que la norme prend environ 2 pages pour décrire le comportement de cet opérateur.

de N-4411:

5.16 opérateur conditionnel

1 groupe d'expressions conditionnel de droite à gauche. La première expression est convertis contextuellement en bool (clause 4). Il est évalué et s'il est vrai, le résultat du conditionnel L'expression est la valeur de la seconde expression, sinon celle de la troisième expression. Seulement un des deuxième et troisième expressions est évalué. Chaque calcul de la valeur et Effet secondaire associé au premier l'expression est séquencée avant chaque Calcul de la valeur et effet secondaire associé au deuxième ou troisième expression.

Aussi, note:

3 sinon, si les deuxième et troisième opérande a différents types, et soit a (éventuellement qualifié de CV) type de classe, une tentative est faite à convertir chacun de ces opérandes vers le Type de l'autre.

L'exemple que vous avez cité a le même type pour les deuxième et troisième expressions - Soyez assuré, seul le premier sera évalué.


1 commentaires

Je doute que le point 3 dit réellement que les deux opérandes seront évaluées. Je pense que cela concerne plutôt si des choses comme std :: string s ("a"); const char * c = "b"; std :: string a = cond ()? S: c; const char * b = cond ()? S: c; doit compiler ou non (déterminer le type de résultat de l'opérateur: le compilateur vérifie ce qui peut être jeté à quoi - dans ce cas le premier?: compile, puisque const Char * peut être implicitement converti en STD :: String, mais la seconde ne compile pas, car le type de résultat de l'opérateur est std :: string , et cela ne peut pas être converti implicitement à const char * )



3
votes

Le code de la division sera généré, d'où l'avertissement. Mais la branche ne sera jamais prise lorsque arg est égal à 0, de sorte qu'il est sûr.


0 commentaires

3
votes

opérateur == pour les numéros de points flottants est dangereux (c'est-à-dire que vous ne pouvez pas y faire confiance, en raison de problèmes d'arrondi). Dans ce cas particulier, il est en fait sûr, vous pouvez donc ignorer l'avertissement, mais le compilateur ne fera pas une telle analyse basée sur un opérateur dont les résultats sont quelque peu imprévisibles dans l'affaire Général.


0 commentaires

3
votes

L'opérateur conditionnel ne doit pas évaluer tous les arguments. Mais je crois que vous pouvez prendre arg presque égale à 0, donc arg == 0.0 sera faux , mais 1./arg donnera "division par zéro" résultat. Donc, je pense que cet avertissement est utile ici.

Au fait, Visual C ++ 2008 ne donne pas un tel avertissement.


3 commentaires

IEEE 754 Les divisions de points flottants en C ne lanceront pas de diviser par zéro. Le résultat de la division par zéro sera + infini, -infinity ou nan


Le résultat peut être + infini ou -infinity pour des nombres sous-formels, mais j'appellerais qu'un débordement plutôt qu'une division par zéro.


@starBlue: presque correct. 1./arg ne peut pas trop déborder. 10.0 / arg pourrait déborder, cependant. (à nouveau en supposant que IEEEE754, 1e0 / 2e-308 = 5E307, qui est <1.7E308)



0
votes

En plus des autres commentaires: L'avertissement génère par le compilateur, la branche morte est supprimée par l'optimiseur qui fonctionne plus tard - éventuellement même à la phase de liaison.

Donc non, ce n'est pas un bug. L'avertissement est un service supplémentaire fourni par le compilateur, non obligatoire par la norme. C'est un effet secondaire malheureux de l'architecture compilation / liante.


0 commentaires

4
votes

C'est un bug évident, au-delà du doute.

L'intention de l'avertissement n'est pas de prévenir toutes les divisions d'un programme. Ce serait beaucoup trop bruyant dans un programme raisonnable. Au lieu de cela, l'intention est de vous avertir lorsque vous devez vérifier un argument. Dans ce cas, vous avez vérifié l'argument. Par conséquent, le compilateur aurait dû noter que et tais-toi.

La mise en oeuvre technique d'une telle fonctionnalité est effectuée par des variables d'étiquetage dans les branches de code avec certains attributs. L'un des attributs les plus courants est le tri-État "est null". Avant la branche, arg est une variable externe et arg [[isnull]] est inconnu. Mais après la vérification sur arg il y a deux branches. Dans la première branche arg [[isnull]] est vrai. Dans la deuxième branche arg [[isnull]] est faux.

Maintenant, lorsqu'il s'agit de générer des avertissements de pointeur divisée et nul, le [[ISNULL] attribut doit être vérifié. Si true, vous avez un avertissement / une erreur sévère. Si inconnu, vous devez générer l'avertissement ci-dessus - un problème potentiel, au-delà de ce que le compilateur peut prouver. Mais dans ce cas, l'attribut [[isnull]] est faux. Le compilateur de la même logique formelle que les humains, sait qu'il n'y a pas de risque.

Mais comment savons-nous que le compilateur utilise un tel [[ISNULL]] attribuer en interne? Rappelez-vous le premier paragraphe: sans cela, il faudrait soit avertir toujours ou jamais. Nous savons qu'il avertit parfois, ergo il doit y avoir un [[isnull]]] attribut.


2 commentaires

C'est un raisonnement solide. Je peux vivre avec ça - précisément puisqu'il prouve mon sentiment de gut.


Un bogue Linux récent a été causé par exactement cet attribut. Dans le code fautif, un pointeur était d'abord alérés (fondamentalement int * Membre = & (Barre FOO->); et ensuite vérifié si (! Foo) retourner; . Toutefois. , en raison de la déséroférence, le FOO [[ISNULL]] est déjà défini sur FALSE, et le chèque null optimisé.



0
votes

Vous pourriez être capable d'éviter l'avertissement en utilisant le mot-clé __ d'assumer Microsoft. Je ne sais pas si vous pouvez l'attacher avec l'opérateur conditionnel. Sinon, quelque chose comme xxx

peut valoir un coup. Ou bien sûr, il suffit de faire taire l'avertissement pendant cette fonction, avec le #pragma approprié .


0 commentaires