Abilitare -Wsign-compare
, come suggerito dalla risposta di FDinoff è una buona idea, ma ho pensato che valesse la pena di spiegare il motivo alla base di alcuni dettagli, poiché è una trappola abbastanza comune.
Il problema non è in realtà con la macro MAX
in particolare, ma con a) sottraendo da un numero intero senza segno in un modo che porta a un overflow, e b) (come suggerisce l'avviso) con come il compilatore gestisce il confronto di valori firmati e non firmati in generale.
Il primo problema è abbastanza semplice da spiegare: quando si sottraggono da un numero intero senza segno e il risultato sarebbe negativo, il risultato "trabocca" su un valore positivo molto grande, poiché un numero intero senza segno non può rappresentare valori negativi. Quindi [@"short" length] - 10
valuterà a 4294967291
.
Quale potrebbe essere più sorprendente è che, anche senza la sottrazione, qualcosa come MAX([@"short" length], -10)
non produrrà il risultato corretto (sarebbe valutare a -10
, anche se [@"short" length]
sarebbe 5
, che è ovviamente più grande). Questo non ha nulla a che fare con la macro, qualcosa come if ([@"short" length] > -10) { ... }
porterebbe allo stesso problema (il codice nel if-block sarebbe non eseguito).
Quindi la domanda generale è: cosa succede esattamente quando confronti un intero senza segno con uno firmato (e perché c'è un avviso per quello in primo luogo)? Il compilatore convertirà entrambi i valori in un tipo comune, in base a determinate regole che possono portare a risultati sorprendenti.
Citando Understand integer conversion rules [cert.org]:
- Se il tipo dell'operando di tipo intero con segno può rappresentare tutti i valori del tipo dell'operando di tipo intero senza segno, l'operando con il tipo intero senza segno è convertito nel tipo di operando con tipo intero con segno.
- In caso contrario, entrambi gli operandi vengono convertiti nel tipo intero senza segno corrispondente al tipo di operando con tipo intero con segno.
(sottolineatura mia)
Considerate questo esempio:
int s = -1;
unsigned int u = 1;
NSLog(@"%i", s < u);
// -> 0
Il risultato sarà 0
(false), anche se s
(-1
) è chiaramente inferiore a u
(1
). Ciò si verifica perché entrambi i valori vengono convertiti in unsigned int
, in quanto int
non può rappresentare tutti i valori che possono essere contenuti in un unsigned int
.
Diventa ancora più confuso se si modifica il tipo di s
in long
. Quindi, otterresti lo stesso risultato (errato) su una piattaforma a 32 bit (iOS), ma in un'applicazione Mac a 64 bit funzionerebbe benissimo! (spiegazione: long
è lì a 64 bit, quindi può rappresentare tutti i 32 bit unsigned int
valori.)
Quindi, per farla breve: non confrontare interi non firmati e con segno, soprattutto se il valore firmato è potenzialmente negativo.
Intendi come 'fmax'? –
Grazie. Mi ha aiutato – Raja