Są trzy powody.
Przede wszystkim start + (end - start) / 2
działa nawet jeśli używasz wskaźników, o ile end - start
nie przepełnia 1 .
int *start = ..., *end = ...;
int *mid = start + (end - start) / 2; // works as expected
int *mid = (start + end) / 2; // type error, won't compile
Po drugie, start + (end - start) / 2
nie przepełni, jeśli start
i end
są dużymi liczbami dodatnimi. W przypadku operandów ze znakiem przepełnienie jest niezdefiniowane:
int start = 0x7ffffffe, end = 0x7fffffff;
int mid = start + (end - start) / 2; // works as expected
int mid = (start + end) / 2; // overflow... undefined
(Pamiętaj, że end - start
może się przepełnić, ale tylko wtedy, gdy start < 0
lub end < 0
.)
Lub w przypadku arytmetyki bez znaku przepełnienie jest zdefiniowane, ale daje złą odpowiedź. Jednak w przypadku operandów bez znaku start + (end - start) / 2
nigdy nie przepełni się tak długo, jak end >= start
.
unsigned start = 0xfffffffeu, end = 0xffffffffu;
unsigned mid = start + (end - start) / 2; // works as expected
unsigned mid = (start + end) / 2; // mid = 0x7ffffffe
Wreszcie, często chcesz zaokrąglić w kierunku start
elementu.
int start = -3, end = 0;
int mid = start + (end - start) / 2; // -2, closer to start
int mid = (start + end) / 2; // -1, surprise!
Przypisy
1 Zgodnie ze standardem C, jeśli wynik odejmowania wskaźnika nie jest reprezentowalny jako a ptrdiff_t
, to zachowanie jest niezdefiniowane. Jednak w praktyce wymaga to przydzielenia char
tablicy zajmującej co najmniej połowę całej przestrzeni adresowej.
(start + end)
może się przepełnić, ale(end - start)
nie może.