Есть три причины.
Прежде всего, start + (end - start) / 2
работает, даже если вы используете указатели, если end - start
не переполняет 1 .
int *start = ..., *end = ...;
int *mid = start + (end - start) / 2; // works as expected
int *mid = (start + end) / 2; // type error, won't compile
Во-вторых, start + (end - start) / 2
не переполнится, если start
и end
будут большие положительные числа. Со знаковыми операндами переполнение не определено:
int start = 0x7ffffffe, end = 0x7fffffff;
int mid = start + (end - start) / 2; // works as expected
int mid = (start + end) / 2; // overflow... undefined
(Обратите внимание, что end - start
может переполниться, но только если start < 0
или end < 0
.)
Или с арифметикой без знака, переполнение определяется, но дает вам неправильный ответ. Однако для неподписанных операндов start + (end - start) / 2
никогда не будет переполнено, пока end >= start
.
unsigned start = 0xfffffffeu, end = 0xffffffffu;
unsigned mid = start + (end - start) / 2; // works as expected
unsigned mid = (start + end) / 2; // mid = 0x7ffffffe
Наконец, вы часто хотите округлить до start
элемента.
int start = -3, end = 0;
int mid = start + (end - start) / 2; // -2, closer to start
int mid = (start + end) / 2; // -1, surprise!
Сноски
1 Согласно стандарту C, если результат вычитания указателя не представляется как a ptrdiff_t
, то поведение не определено. Однако на практике это требует выделения char
массива, используя как минимум половину всего адресного пространства.
(start + end)
может переполниться, а(end - start)
не может.