... การลดทอนตัวชี้นอกช่วงที่จัดสรรนั้นดูเหมือนว่าจะเป็นภาพร่างที่สมบูรณ์สำหรับฉัน นี่เป็นพฤติกรรม "อนุญาต" ใน C หรือไม่
อนุญาตหรือไม่ ใช่. ความคิดที่ดี? ไม่ปกติ
C เป็นชวเลขสำหรับภาษาแอสเซมบลีและในภาษาแอสเซมบลีไม่มีพอยน์เตอร์เพียงที่อยู่หน่วยความจำ พอยน์เตอร์ของ C คือที่อยู่หน่วยความจำที่มีพฤติกรรมด้านข้างของการเพิ่มหรือลดลงตามขนาดของสิ่งที่ชี้ไปเมื่ออยู่ภายใต้เลขคณิต ทำให้สิ่งต่อไปนี้เป็นเรื่องปกติจากมุมมองของไวยากรณ์:
double *p = (double *)0xdeadbeef;
--p; // p == 0xdeadbee7, assuming sizeof(double) == 8.
double d = p[0];
อาร์เรย์ไม่ใช่สิ่งที่อยู่ใน C พวกมันเป็นเพียงพอยน์เตอร์สำหรับช่วงของหน่วยความจำต่อเนื่องที่ทำงานเหมือนอาร์เรย์ []
ประกอบการจดชวเลขสำหรับการทำเลขคณิตชี้และ dereferencing ดังนั้นจริงหมายถึงa[x]
*(a + x)
มีเหตุผลที่ถูกต้องในการทำข้างต้นเช่นอุปกรณ์ I / O บางคนคู่ของมีdouble
s แมปลงและ0xdeadbee7
0xdeadbeef
โปรแกรมน้อยมากที่จะต้องทำเช่นนั้น
เมื่อคุณสร้างที่อยู่ของบางสิ่งเช่นโดยใช้&
โอเปอเรเตอร์หรือการโทรmalloc()
คุณต้องการรักษาตัวชี้ดั้งเดิมให้คงอยู่เพื่อที่คุณจะได้รู้ว่าสิ่งที่ชี้ไปนั้นเป็นสิ่งที่ใช้ได้จริง การลดพอยน์เตอร์ตัวชี้หมายความว่าโค้ดที่ผิดพลาดบางอย่างสามารถลองอ้างถึงได้รับผลลัพธ์ที่ผิดพลาดการอุดตันบางอย่างหรือขึ้นอยู่กับสภาพแวดล้อมของคุณที่กระทำการละเมิดการแบ่งกลุ่ม นี่เป็นเรื่องจริงโดยเฉพาะอย่างยิ่งmalloc()
เพราะคุณให้ภาระกับใครก็ตามที่โทรfree()
มาเพื่อจำไว้ว่าให้ส่งผ่านค่าดั้งเดิมและไม่ใช่เวอร์ชั่นที่เปลี่ยนแปลงซึ่งจะทำให้ทุกคนแตกหัก
หากคุณต้องการ 1-based arrays ใน C คุณสามารถทำได้อย่างปลอดภัยด้วยค่าใช้จ่ายในการจัดสรรองค์ประกอบเพิ่มเติมอีกหนึ่งองค์ประกอบที่จะไม่ถูกใช้งาน:
double *array_create(size_t size) {
// Wasting one element, so don't allow it to be full-sized
assert(size < SIZE_MAX);
return malloc((size+1) * sizeof(double));
}
inline double array_index(double *array, size_t index) {
assert(array != NULL);
assert(index >= 1); // This is a 1-based array
return array[index];
}
โปรดทราบว่าสิ่งนี้ไม่ได้ทำอะไรเลยเพื่อป้องกันเกินขอบเขตบน แต่ก็ง่ายพอที่จะรับมือ
ภาคผนวก:
บางบทและร้อยกรองจากC99 ฉบับร่าง (ขออภัยนั่นคือทั้งหมดที่ฉันสามารถเชื่อมโยงไป):
§6.5.2.1.1บอกว่านิพจน์ที่สอง ("อื่น ๆ ") ที่ใช้กับโอเปอเรเตอร์ตัวห้อยเป็นประเภทจำนวนเต็ม -1
เป็นจำนวนเต็มและทำให้p[-1]
ถูกต้องและทำให้ตัวชี้&(p[-1])
ใช้ได้เช่นกัน นี่ไม่ได้หมายความว่าการเข้าถึงหน่วยความจำในตำแหน่งนั้นจะสร้างพฤติกรรมที่กำหนด แต่ตัวชี้ยังคงเป็นตัวชี้ที่ถูกต้อง
§6.5.2.2บอกว่าอาร์เรย์ห้อยประเมินผู้ประกอบการที่จะเทียบเท่าของการเพิ่มจำนวนองค์ประกอบที่จะชี้จึงจะเทียบเท่ากับp[-1]
*(p + (-1))
ยังคงใช้ได้ แต่อาจไม่ก่อให้เกิดพฤติกรรมที่พึงประสงค์
§6.5.6.8พูดว่า (เน้นการทำเหมือง):
เมื่อนิพจน์ที่มีชนิดจำนวนเต็มถูกเพิ่มหรือลบออกจากตัวชี้ผลลัพธ์จะมีชนิดของตัวถูกดำเนินการตัวชี้
... ถ้านิพจน์P
ชี้ไปที่i
องค์ประกอบ -th ของวัตถุอาร์เรย์นิพจน์(P)+N
(เท่ากันN+(P)
) และ(P)-N
(โดยที่N
มีค่าn
) ชี้ไปที่ตามลำดับองค์ประกอบที่ -th i+n
และ
i−n
-th ของวัตถุอาร์เรย์หากพวกเขามีอยู่ .
ซึ่งหมายความว่าผลลัพธ์ของการคำนวณทางคณิตศาสตร์ต้องชี้ไปที่องค์ประกอบในอาร์เรย์ ไม่ได้บอกว่าต้องใช้เลขคณิตทั้งหมดในครั้งเดียว ดังนั้น:
double a[20];
// This points to element 9 of a; behavior is defined.
double d = a[-1 + 10];
double *p = a - 1; // This is just a pointer. No dereferencing.
double e = p[0]; // Does not point at any element of a; behavior is undefined.
double f = p[1]; // Points at element 0 of a; behavior is defined.
ฉันแนะนำให้ทำสิ่งนี้ด้วยวิธีนี้หรือไม่? ฉันทำไม่ได้และคำตอบของฉันอธิบายว่าทำไม