มีสามเหตุผล
ครั้งแรกของทั้งหมดstart + (end - start) / 2
ทำงานแม้ว่าคุณจะใช้ตัวชี้ตราบใดที่end - start
ไม่ได้ล้น1
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 หากผลลัพธ์ของการลบตัวชี้ไม่สามารถแทนได้ว่าเป็น a ptrdiff_t
ดังนั้นพฤติกรรมจะไม่ได้กำหนด อย่างไรก็ตามในทางปฏิบัติสิ่งนี้ต้องการการจัดสรรchar
อาร์เรย์โดยใช้พื้นที่ที่อยู่ทั้งหมดอย่างน้อยครึ่งหนึ่ง
(start + end)
อาจล้นในขณะที่(end - start)
ไม่สามารถ