Ответ 1
Включение -Wsign-compare
, как было предложено с помощью ответа FDinoff, является хорошей идеей, но я подумал, что, возможно, стоит объяснить причину этого более подробно, поскольку это довольно распространенная ошибка.
Проблема не связана с макросом MAX
в частности, а с a) вычитанием из целого числа без знака способом, который приводит к переполнению, и b) (как следует из предупреждения) о том, как обрабатывается компилятор сравнение значений подписи и без знака в целом.
Первая проблема довольно легко объяснить: когда вы вычитаете из целого числа без знака и результат будет отрицательным, результат "переполняется" до очень большого положительного значения, поскольку целое число без знака не может представлять отрицательные значения. Поэтому [@"short" length] - 10
будет оцениваться как 4294967291
.
Что может быть более удивительным, так это то, что даже без вычитания нечто вроде MAX([@"short" length], -10)
не даст правильного результата (он будет оценивать до -10
, хотя [@"short" length]
будет 5
, что, очевидно, больше). Это не имеет ничего общего с макросом, что-то вроде if ([@"short" length] > -10) { ... }
приведет к одной и той же проблеме (код в if-блоке не будет выполняться).
Итак, общий вопрос: что происходит именно тогда, когда вы сравниваете целое число без знака с подписанным (и почему это предупреждение для этого в первую очередь)? Компилятор преобразует оба значения в общий тип в соответствии с определенными правилами, которые могут привести к неожиданным результатам.
Цитата из Понимать целые правила преобразования [cert.org]:
- Если тип операнда со знаком целочисленного типа может представлять все значения типа операнда с целым числом без знака, операнд с целым числом без знака преобразуется в тип операнда со знаком целочисленного типа.
- В противном случае оба операнда преобразуются в целочисленный тип без знака, соответствующий типу операнда со знаком целочисленного типа.
(акцент мой)
Рассмотрим следующий пример:
int s = -1;
unsigned int u = 1;
NSLog(@"%i", s < u);
// -> 0
Результат будет 0
(false), хотя s
(-1
) явно меньше, чем u
(1
). Это происходит потому, что оба значения преобразуются в unsigned int
, поскольку int
не может представлять все значения, которые могут содержаться в unsigned int
.
Это становится еще более запутанным, если вы меняете тип s
на long
. Затем вы получите тот же (неверный) результат на 32-битной платформе (iOS), но в 64-битном Mac-приложении это будет работать нормально! (объяснение: long
- это 64-битный тип, поэтому он может представлять все 32-битные значения unsigned int
.)
Итак, длинный рассказ: не сравнивайте целые числа без знака и знака, особенно если подписанное значение потенциально отрицательно.