Ci sono tre ragioni.
Prima di tutto, start + (end - start) / 2
funziona anche se si utilizzano i puntatori, purché end - start
non trabocchi 1 .
int *start = ..., *end = ...;
int *mid = start + (end - start) / 2; // works as expected
int *mid = (start + end) / 2; // type error, won't compile
In secondo luogo, start + (end - start) / 2
non traboccerà se start
e end
sono grandi numeri positivi. Con gli operandi firmati, l'overflow non è definito:
int start = 0x7ffffffe, end = 0x7fffffff;
int mid = start + (end - start) / 2; // works as expected
int mid = (start + end) / 2; // overflow... undefined
(Nota che end - start
potrebbe traboccare, ma solo se start < 0
o end < 0
.)
O con aritmetica senza segno, l'overflow è definito ma ti dà la risposta sbagliata. Tuttavia, per gli operandi non firmati, start + (end - start) / 2
non sarà mai in overflow fino a quando end >= start
.
unsigned start = 0xfffffffeu, end = 0xffffffffu;
unsigned mid = start + (end - start) / 2; // works as expected
unsigned mid = (start + end) / 2; // mid = 0x7ffffffe
Infine, spesso vuoi arrotondare verso l' start
elemento.
int start = -3, end = 0;
int mid = start + (end - start) / 2; // -2, closer to start
int mid = (start + end) / 2; // -1, surprise!
Le note
1 Secondo lo standard C, se il risultato della sottrazione del puntatore non è rappresentabile come a ptrdiff_t
, il comportamento non è definito. Tuttavia, in pratica, ciò richiede l'allocazione di un char
array utilizzando almeno metà dell'intero spazio degli indirizzi.
(start + end)
potrebbe traboccare, mentre(end - start)
non può.