Có ba lý do.
Trước hết, start + (end - start) / 2hoạt động ngay cả khi bạn đang sử dụng con trỏ, miễn là end - startkhông tràn 1 .
int *start = ..., *end = ...;
int *mid = start + (end - start) / 2; // works as expected
int *mid = (start + end) / 2; // type error, won't compile
Thứ hai, start + (end - start) / 2sẽ không tràn nếu startvà endlà số dương lớn. Với các toán hạng đã ký, tràn không được xác định:
int start = 0x7ffffffe, end = 0x7fffffff;
int mid = start + (end - start) / 2; // works as expected
int mid = (start + end) / 2; // overflow... undefined
(Lưu ý rằng end - startcó thể tràn, nhưng chỉ khi start < 0hoặc end < 0.)
Hoặc với số học không dấu, tràn được xác định nhưng cung cấp cho bạn câu trả lời sai. Tuy nhiên, đối với các toán hạng không dấu, start + (end - start) / 2sẽ không bao giờ tràn ra miễn là end >= start.
unsigned start = 0xfffffffeu, end = 0xffffffffu;
unsigned mid = start + (end - start) / 2; // works as expected
unsigned mid = (start + end) / 2; // mid = 0x7ffffffe
Cuối cùng, bạn thường muốn làm tròn về phía startphần tử.
int start = -3, end = 0;
int mid = start + (end - start) / 2; // -2, closer to start
int mid = (start + end) / 2; // -1, surprise!
Chú thích
1 Theo tiêu chuẩn C, nếu kết quả của phép trừ con trỏ không thể biểu diễn dưới dạng a ptrdiff_t, thì hành vi không được xác định. Tuy nhiên, trong thực tế, điều này đòi hỏi phải phân bổ một charmảng bằng cách sử dụng ít nhất một nửa toàn bộ không gian địa chỉ.
(start + end)có thể tràn, trong khi(end - start)không thể.