理由は3つあります。
まず、オーバーフローしないstart + (end - start) / 2
限り、ポインターを使用している場合でも機能します1。end - start
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標準によると、ポインター減算の結果がとして表現できないptrdiff_t
場合、動作は未定義です。ただし、実際にchar
は、アドレス空間全体の少なくとも半分を使用して配列を割り当てる必要があります。
(start + end)
オーバーフローする可能性がありますが、オーバーフロー(end - start)
することはありません。