Є три причини.
Перш за все, 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 Відповідно до стандарту С, якщо результат віднімання вказівника не представлений як a ptrdiff_t, то поведінка не визначена. Однак на практиці для цього потрібно виділити charмасив, використовуючи принаймні половину всього адресного простору.
(start + end)може переповнюватись, а поки(end - start)не може.