สำนวนนี้หลุดออกจากการจัดสรรอาร์เรย์ 1D โดยธรรมชาติ เริ่มต้นด้วยการจัดสรรอาร์เรย์ 1D ของบางประเภทโดยพลการT
:
T *p = malloc( sizeof *p * N );
ง่ายใช่มั้ย? แสดงออก *p
มีประเภทT
เพื่อsizeof *p
ให้ผลเช่นเดียวsizeof (T)
ดังนั้นเราจึงจัดสรรพื้นที่เพียงพอสำหรับอาร์เรย์องค์ประกอบของN
T
นี้เป็นจริงสำหรับประเภทใด ๆT
ตอนนี้ให้แทนของกับชนิดอาร์เรย์เช่นT
R [10]
จากนั้นการจัดสรรของเราจะกลายเป็น
R (*p)[10] = malloc( sizeof *p * N);
ความหมายตรงนี้เหมือนกับวิธีการจัดสรร 1D สิ่งที่เปลี่ยนไปคือประเภทของp
. แทนที่จะก็ตอนนี้T *
R (*)[10]
การแสดงออกที่*p
มีประเภทT
ซึ่งเป็นประเภทR [10]
เพื่อให้sizeof *p
เทียบเท่ากับซึ่งเทียบเท่ากับsizeof (T)
sizeof (R [10])
ดังนั้นเรากำลังการจัดสรรพื้นที่เพียงพอสำหรับN
โดยอาร์เรย์องค์ประกอบของ 10
R
เราสามารถนำสิ่งนี้ไปได้ไกลกว่านี้ถ้าเราต้องการ สมมติว่าเป็นตัวเองชนิดอาร์เรย์R
int [5]
แทนสิ่งนั้นR
และเราได้รับ
int (*p)[10][5] = malloc( sizeof *p * N);
จัดการกัน - sizeof *p
เป็นเช่นเดียวกับsizeof (int [10][5])
เราและเราไขลานจัดสรรก้อนที่ต่อเนื่องกันของหน่วยความจำขนาดใหญ่พอที่จะถือN
โดย10
โดยอาร์เรย์ของ 5
int
นั่นคือด้านการจัดสรร แล้วด้านการเข้าถึงล่ะ?
โปรดจำไว้ว่า[]
การดำเนินการห้อยถูกกำหนดไว้ในแง่ของการคำนวณตัวชี้: a[i]
ถูกกำหนดให้เป็น1*(a + i)
ดังนั้นตัวดำเนินการตัวห้อยจึงหมายถึงตัวชี้[]
โดยปริยาย หากp
เป็นตัวชี้ไปT
คุณสามารถเข้าถึงค่าที่ชี้ไปที่ใดก็ได้โดยการยกเลิกการอ้างอิงอย่างชัดเจนด้วยตัวดำเนิน*
การยูนารี:
T x = *p;
หรือโดยใช้ตัว[]
ดำเนินการตัวห้อย:
T x = p[0]; // identical to *p
ดังนั้นหากp
ชี้ไปที่องค์ประกอบแรกของอาร์เรย์คุณสามารถเข้าถึงองค์ประกอบใด ๆ ของอาร์เรย์นั้นได้โดยใช้ตัวห้อยบนตัวชี้p
:
T arr[N];
T *p = arr; // expression arr "decays" from type T [N] to T *
...
T x = p[i]; // access the i'th element of arr through pointer p
ตอนนี้เรามาทำการแทนที่ของเราอีกครั้งและแทนที่T
ด้วยประเภทอาร์เรย์R [10]
:
R arr[N][10];
R (*p)[10] = arr; // expression arr "decays" from type R [N][10] to R (*)[10]
...
R x = (*p)[i];
ความแตกต่างที่เห็นได้ชัดในทันที เรากำลังอ้างถึงอย่างชัดเจนp
ก่อนที่จะใช้ตัวดำเนินการตัวห้อย เราไม่ต้องการตัวห้อยp
เราต้องการห้อยลงในสิ่งที่p
ชี้ไป (ในกรณีนี้คืออาร์เรย์ arr[0]
) ตั้งแต่เอก*
มีความสำคัญต่ำกว่าห้อย[]
ประกอบการที่เราจะต้องใช้วงเล็บอย่างชัดเจนในกลุ่มด้วยp
*
แต่จำไว้ว่าจากด้านบนนั้น*p
เหมือนกับp[0]
เราจึงสามารถแทนที่ด้วย
R x = (p[0])[i];
หรือเพียงแค่
R x = p[0][i];
ดังนั้นหากp
ชี้ไปที่อาร์เรย์ 2 มิติเราสามารถจัดทำดัชนีในอาร์เรย์p
นั้นได้ดังนี้:
R x = p[i][j]; // access the i'th element of arr through pointer p;
// each arr[i] is a 10-element array of R
นำสิ่งนี้ไปสู่ข้อสรุปเดียวกันกับด้านบนและแทนที่R
ด้วยint [5]
:
int arr[N][10][5];
int (*p)[10][5]; // expression arr "decays" from type int [N][5][10] to int (*)[10][5]
...
int x = p[i][j][k];
นี้ทำงานเพียงเดียวกันถ้าชี้ไปยังอาร์เรย์ปกติหรือถ้ามันชี้ไปจัดสรรหน่วยความจำผ่าน p
malloc
สำนวนนี้มีประโยชน์ดังต่อไปนี้:
- มันง่ายมาก - โค้ดเพียงบรรทัดเดียวซึ่งต่างจากวิธีการจัดสรรทีละน้อย
T **arr = malloc( sizeof *arr * N );
if ( arr )
{
for ( size_t i = 0; i < N; i++ )
{
arr[i] = malloc( sizeof *arr[i] * M );
}
}
- แถวทั้งหมดของอาร์เรย์ที่จัดสรรคือ * ต่อเนื่องกัน * ซึ่งไม่ใช่กรณีของวิธีการจัดสรรทีละน้อยข้างต้น
- deallocating
free
อาร์เรย์เป็นเพียงเป็นเรื่องง่ายด้วยสายเดียวที่จะ อีกครั้งไม่เป็นความจริงกับวิธีการจัดสรรทีละน้อยซึ่งคุณต้องยกเลิกการจัดสรรแต่ละรายการarr[i]
ก่อนจึงจะสามารถยกเลิกการจัดสรรarr
ได้
บางครั้งวิธีการจัดสรรแบบทีละชิ้นจะดีกว่าเช่นเมื่อฮีปของคุณแยกส่วนไม่ดีและคุณไม่สามารถจัดสรรหน่วยความจำของคุณเป็นชิ้นส่วนที่ต่อเนื่องกันได้หรือคุณต้องการจัดสรรอาร์เรย์แบบ "หยัก" ซึ่งแต่ละแถวสามารถมีความยาวต่างกันได้ แต่โดยทั่วไปแล้วนี่เป็นวิธีที่ดีกว่า
1. โปรดจำไว้ว่าอาร์เรย์ไม่ใช่ตัวชี้ แต่นิพจน์อาร์เรย์จะถูกแปลงเป็นนิพจน์พอยน์เตอร์ตามความจำเป็น