Fonction MAX / MIN en Objective C, éviter de jeter les questions
J'ai eu le code de mon application qui ressemble à la suivante. J'ai eu quelques commentaires autour d'un bug, quand à ma grande horreur, j'ai mis un débogueur sur elle et a constaté que le MAX entre -5 et 0 -5!
NSString *test = @"short";
int calFailed = MAX(test.length - 10, 0); //returns -5
Après avoir regardé le MAX de macro, je vois qu'il exige à la fois des paramètres de même type. Dans mon cas, "test.longueur" est un unsigned int et 0 est une signature de type int. Ainsi, un simple cast (pour chaque paramètre) résout le problème.
NSString *test = @"short";
int calExpected = MAX((int)test.length - 10, 0); //returns 0
Cela semble être un méchant et inattendus de des effets secondaires de cette macro. Est-il une autre méthode intégrée à iOS pour effectuer des MIN/MAX, où le compilateur aurait mis en garde sur le décalage des types? Semble que ce DEVRAIT ont été un moment de la compilation problème et non pas quelque chose qui nécessitait un débogueur à comprendre. Je peux toujours écrire mon propre, mais je voulais voir si quelqu'un d'autre a eu des problèmes similaires.
- Tu veux dire
fmax
? - Je vous remercie. Cela m'a aidé.
Vous devez vous connecter pour publier un commentaire.
Permettant
-Wsign-compare
, comme suggéré par FDinoff réponse est une bonne idée, mais j'ai pensé qu'il pourrait être utile d'en expliquer la raison derrière cela un peu plus en détail, car c'est un piège.Le problème n'est pas vraiment avec le
MAX
macro en particulier, mais avec une) la soustraction d'un entier non signé dans un chemin qui conduit à un dépassement de capacité, et b) (comme l'avertissement à l'indique) avec la manière dont le compilateur gère la comparaison des entiers signés et non signés valeurs en général.La première question est assez facile à expliquer: Lorsque vous soustrayez à partir d'un entier non signé, le résultat serait négatif, le résultat "débordements" à une très grande valeur positive, car un entier non signé ne peut pas représenter des valeurs négatives. Donc
[@"short" length] - 10
permettra d'évaluer à4294967291
.Ce qui pourrait être plus surprenant, c'est que même sans la soustraction, quelque chose comme
MAX([@"short" length], -10)
ne donnera pas le résultat correct (il serait d'évaluer la-10
, même si[@"short" length]
serait5
, ce qui est évidemment plus). Cela n'a rien à voir avec la macro, quelque chose commeif ([@"short" length] > -10) { ... }
conduirait à le même problème (le code dans le si-bloc pas exécuter).Donc, la question générale est: qu'est Ce qui se passe exactement lorsque vous comparez un entier non signé signé un (et pourquoi est-il un avertissement pour que, en premier lieu)? Le compilateur se convertir à la fois des valeurs d'un type commun, selon certaines règles qui peuvent conduire à des résultats surprenants.
Citant Comprendre entier règles de conversion [cert.org]:
(l'emphase est mienne)
Considérons cet exemple:
Le résultat sera
0
(faux), même sis
(-1
) est clairement moinsu
(1
). Cela se produit parce que les deux valeurs sont convertiesunsigned int
, commeint
ne peut pas représenter toutes les valeurs qui peuvent être contenues dans ununsigned int
.Il obtient encore plus de confusion, si vous modifiez le type de
s
àlong
. Ensuite, vous obtiendrez le même (mauvais) résultat sur 32 bits plate-forme (iOS), mais dans une version 64 bits Mac app il serait très bien fonctionner! (explication:long
est un 64 bits type de il y, de sorte qu'il peut représenter la totalité des 32 bitsunsigned int
valeurs).Donc, longue histoire courte: Ne comparez pas non signés et les entiers signés, en particulier si la valeur signée est potentiellement négatifs.
Vous n'avez probablement pas assez d'avertissements du compilateur sous tension. Si vous tournez sur
-Wsign-compare
(qui peut être activé avec-Wextra
), vous allez générer un avertissement qui ressemble à la suivanteCela vous permet de placer le jette à la bonne place, si nécessaire, et vous ne devriez pas avoir à réécrire le MAX ou MIN macros
-Wall
de tourner sur plus d'avertissement du compilateur si vous ne l'avez pas déjàclang
au lieu degcc
. De sorte que vous pouvez faire mieux que-Wall
: tour sur-Weverything
pour tous les avertissements. Ensuite, essayez-Werror
pour le Hard Mode de réalisation.