Có ba lý do.
Trước hết, start + (end - start) / 2
hoạt động ngay cả khi bạn đang sử dụng con trỏ, miễn là end - start
khô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) / 2
sẽ không tràn nếu start
và end
là 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 - start
có thể tràn, nhưng chỉ khi start < 0
hoặ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) / 2
sẽ 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 start
phầ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 char
mả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ể.