Existem três razões.
Primeiro de tudo, start + (end - start) / 2
funciona mesmo se você estiver usando ponteiros, desde end - start
que não exceda 1 .
int *start = ..., *end = ...;
int *mid = start + (end - start) / 2; // works as expected
int *mid = (start + end) / 2; // type error, won't compile
Segundo, start + (end - start) / 2
não transbordará se start
e end
for um grande número positivo. Com operandos assinados, o estouro é indefinido:
int start = 0x7ffffffe, end = 0x7fffffff;
int mid = start + (end - start) / 2; // works as expected
int mid = (start + end) / 2; // overflow... undefined
(Observe que end - start
pode estourar, mas apenas se start < 0
ou end < 0
.)
Ou com aritmética não assinada, o excesso é definido, mas fornece a resposta errada. No entanto, para operandos não assinados, start + (end - start) / 2
nunca excederá o tempo end >= start
.
unsigned start = 0xfffffffeu, end = 0xffffffffu;
unsigned mid = start + (end - start) / 2; // works as expected
unsigned mid = (start + end) / 2; // mid = 0x7ffffffe
Por fim, muitas vezes você deseja arredondar para o start
elemento.
int start = -3, end = 0;
int mid = start + (end - start) / 2; // -2, closer to start
int mid = (start + end) / 2; // -1, surprise!
Notas de rodapé
1 De acordo com o padrão C, se o resultado da subtração do ponteiro não for representável como a ptrdiff_t
, o comportamento será indefinido. No entanto, na prática, isso requer a alocação de uma char
matriz usando pelo menos metade do espaço de endereço inteiro.
(start + end)
pode estourar, enquanto(end - start)
não pode.