Włączenie , zgodnie z sugestią FDinoffa, jest dobrym pomysłem, ale pomyślałem, że warto wyjaśnić przyczynę tego bardziej szczegółowo, ponieważ jest to dość powszechna pułapka.
Problem nie dotyczy w szczególności makra MAX
, ale z a) odejmowaniem od liczby całkowitej bez znaku w sposób, który prowadzi do przepełnienia, oraz b) (jak sugeruje ostrzeżenie) z tym, jak kompilator obsługuje porównanie ogólnie wartości podpisanych i niepodpisanych.
Pierwszy problem jest dość łatwy do wytłumaczenia: Gdy odejmiesz od liczby całkowitej bez znaku, a wynik będzie ujemny, wynik "przepełni się" do bardzo dużej wartości dodatniej, ponieważ liczba całkowita bez znaku nie może reprezentować wartości ujemnych. Tak więc [@"short" length] - 10
oceni na 4294967291
.
Co może być bardziej zaskakujące jest to, że nawet bez odejmowania, coś MAX([@"short" length], -10)
nie przyniesie poprawny wynik (to ocenić na -10
, choć [@"short" length]
byłoby 5
, co jest oczywiście większe). To nie ma nic wspólnego z makrem, coś takiego jak if ([@"short" length] > -10) { ... }
doprowadziłoby do tego samego problemu (kod w bloku if wykonałby się , a nie).
Tak więc ogólne pytanie brzmi: co dzieje się dokładnie podczas porównywania liczby całkowitej bez znaku z podpisaną (i dlaczego w ogóle jest to ostrzeżenie)? Kompilator skonwertuje obie wartości do wspólnego typu, zgodnie z pewnymi regułami, które mogą prowadzić do zaskakujących wyników.
Cytując Understand integer conversion rules [cert.org]:
- Jeśli typ operandu z podpisanym typu całkowitego mogą reprezentować wszystkie wartości typu argumentu z unsigned integer, operand z unsigned integer przekonwertowany na typ operandu ze znakiem typu integer.
- W przeciwnym razie oba operandy są konwertowane na liczbę całkowitą typu bez znaku, odpowiadającą typowi operandu ze znakiem ze znakiem liczby całkowitej.
(Kopalnia nacisk)
Rozważmy następujący przykład:
int s = -1;
unsigned int u = 1;
NSLog(@"%i", s < u);
// -> 0
wynik będzie 0
(fałsz), choć s
(-1
) jest wyraźnie mniejsza niż u
(1
). Dzieje się tak, ponieważ obie wartości są konwertowane na unsigned int
, ponieważ int
nie może reprezentować wszystkich wartości, które mogą być zawarte w unsigned int
.
To staje się jeszcze bardziej zagmatwane, jeśli zmienisz typ s
na long
. Następnie uzyskasz taki sam (niepoprawny) wynik na platformie 32-bitowej (iOS), ale w 64-bitowej aplikacji Mac będzie działać dobrze! (Wyjaśnienie: long
jest 64 bitowy typ tam, więc może reprezentować wszystkie 32 bitowe unsigned int
wartości.)
Więc, krótko mówiąc: Nie porównuj niepodpisane i podpisane liczb całkowitych, zwłaszcza jeśli podpisane wartość jest potencjalnie negatywne.
Masz na myśli jak "fmax"? –
Dziękuję. Pomogło mi to. – Raja