ขนาดคงที่
1. ผ่านการอ้างอิง
template <size_t rows, size_t cols>
void process_2d_array_template(int (&array)[rows][cols])
{
std::cout << __func__ << std::endl;
for (size_t i = 0; i < rows; ++i)
{
std::cout << i << ": ";
for (size_t j = 0; j < cols; ++j)
std::cout << array[i][j] << '\t';
std::cout << std::endl;
}
}
ใน C ++ ผ่านอาร์เรย์โดยการอ้างอิงโดยไม่สูญเสียข้อมูลมิติน่าจะปลอดภัยที่สุดเนื่องจากผู้ใช้ไม่จำเป็นต้องกังวลเกี่ยวกับผู้เรียกที่ส่งมิติที่ไม่ถูกต้อง อย่างไรก็ตามสิ่งนี้ไม่สามารถทำได้ด้วยอาเรย์ (freestore) แบบไดนามิก ใช้งานได้สำหรับอาร์เรย์อัตโนมัติ ( โดยปกติคือสแต็ก - ลิฟวิ่ง) เท่านั้นเช่นมิติข้อมูลที่ควรทราบ ณ เวลารวบรวม
2. ผ่านตัวชี้
void process_2d_array_pointer(int (*array)[5][10])
{
std::cout << __func__ << std::endl;
for (size_t i = 0; i < 5; ++i)
{
std::cout << i << ": ";
for (size_t j = 0; j < 10; ++j)
std::cout << (*array)[i][j] << '\t';
std::cout << std::endl;
}
}
เทียบเท่า C ของวิธีการก่อนหน้านี้จะผ่านอาร์เรย์โดยตัวชี้ สิ่งนี้ไม่ควรสับสนกับการผ่านประเภทตัวชี้ที่ลดลงของอาร์เรย์(3)ซึ่งเป็นวิธีการทั่วไปที่เป็นที่นิยมแม้ว่าจะปลอดภัยน้อยกว่าอันนี้ แต่มีความยืดหยุ่นมากกว่า เช่นเดียวกับ(1)ใช้วิธีนี้เมื่อมิติทั้งหมดของอาเรย์ได้รับการแก้ไขและรู้ ณ เวลารวบรวม ทราบว่าเมื่อเรียกใช้ฟังก์ชันที่อยู่ของอาเรย์ควรจะผ่านไปและไม่ได้อยู่ขององค์ประกอบแรกจากการสลายตัวprocess_2d_array_pointer(&a)
process_2d_array_pointer(a)
ขนาดตัวแปร
สิ่งเหล่านี้สืบทอดมาจาก C แต่มีความปลอดภัยน้อยกว่าคอมไพเลอร์ไม่มีวิธีการตรวจสอบรับประกันว่าผู้โทรผ่านมิติที่ต้องการ ฟังก์ชั่นจะแสดงเฉพาะสิ่งที่ผู้โทรผ่านเป็นมิติ เหล่านี้มีความยืดหยุ่นมากกว่าข้างต้นเนื่องจากอาร์เรย์ที่มีความยาวต่างกันสามารถส่งผ่านไปยังพวกมันได้อย่างคงเส้นคงวา
โปรดจำไว้ว่าไม่มีสิ่งเช่นการส่งอาร์เรย์โดยตรงไปยังฟังก์ชันใน C [ในขณะที่ใน C ++ พวกเขาสามารถส่งผ่านเป็นการอ้างอิง(1) ]; (2)กำลังส่งตัวชี้ไปยังอาร์เรย์และไม่ใช่อาร์เรย์เอง เสมอผ่านอาร์เรย์เป็นคือจะกลายเป็นตัวชี้การดำเนินการคัดลอกซึ่งจะอำนวยความสะดวกโดยธรรมชาติอาร์เรย์ของเข้าสลายตัวชี้
3. การผ่าน (ค่า) ตัวชี้ไปยังชนิดที่มีการผุ
// int array[][10] is just fancy notation for the same thing
void process_2d_array(int (*array)[10], size_t rows)
{
std::cout << __func__ << std::endl;
for (size_t i = 0; i < rows; ++i)
{
std::cout << i << ": ";
for (size_t j = 0; j < 10; ++j)
std::cout << array[i][j] << '\t';
std::cout << std::endl;
}
}
แม้ว่าint array[][10]
จะได้รับอนุญาตฉันไม่แนะนำมันเหนือไวยากรณ์ข้างต้นเนื่องจากไวยากรณ์ข้างต้นทำให้ชัดเจนว่าตัวระบุarray
เป็นตัวชี้เดียวกับอาร์เรย์จำนวนเต็ม 10 ตัวในขณะที่ไวยากรณ์นี้ดูเหมือนว่าเป็นอาร์เรย์ 2 มิติ แต่เป็นตัวชี้เดียวกันกับ อาร์เรย์ของจำนวนเต็ม 10 จำนวน ที่นี่เรารู้จำนวนองค์ประกอบในแถวเดียว (เช่นขนาดคอลัมน์ 10 ที่นี่) แต่ไม่ทราบจำนวนแถวและจะถูกส่งผ่านเป็นอาร์กิวเมนต์ ในกรณีนี้มีความปลอดภัยเนื่องจากคอมไพเลอร์สามารถตั้งค่าสถานะเมื่อตัวชี้ไปยังอาร์เรย์ที่มีมิติที่สองไม่เท่ากับ 10 ถูกส่งผ่าน มิติแรกคือส่วนต่าง ๆ และสามารถละเว้นได้ ดูที่นี่สำหรับเหตุผลที่ว่าทำไมจึงอนุญาตให้ละเว้นเฉพาะมิติแรกเท่านั้น
4. ผ่านตัวชี้ไปยังตัวชี้
// int *array[10] is just fancy notation for the same thing
void process_pointer_2_pointer(int **array, size_t rows, size_t cols)
{
std::cout << __func__ << std::endl;
for (size_t i = 0; i < rows; ++i)
{
std::cout << i << ": ";
for (size_t j = 0; j < cols; ++j)
std::cout << array[i][j] << '\t';
std::cout << std::endl;
}
}
อีกครั้งมีไวยากรณ์ทางเลือกของการที่เป็นเช่นเดียวกับint *array[10]
int **array
ในไวยากรณ์นี้จะถูกละเว้นในขณะที่มันสูญสลายลงไปในตัวชี้จึงกลายเป็น[10]
int **array
อาจเป็นเพียงคิวสำหรับผู้โทรที่อาเรย์ที่ผ่านควรมีอย่างน้อย 10 คอลัมน์แม้จะต้องมีการนับจำนวนแถว ในกรณีใด ๆ คอมไพเลอร์ไม่ได้ตั้งค่าสถานะสำหรับการละเมิดความยาว / ขนาดใด ๆ (มันจะตรวจสอบเฉพาะประเภทที่ส่งผ่านเป็นตัวชี้ไปยังตัวชี้) ดังนั้นจึงต้องมีทั้งแถวและคอลัมน์นับเป็นพารามิเตอร์ทำให้รู้สึกที่นี่
หมายเหตุ: (4) เป็นตัวเลือกที่ปลอดภัยที่สุดเนื่องจากแทบจะไม่มีการตรวจสอบประเภทใด ๆ และไม่สะดวกที่สุด เราไม่สามารถผ่านอาร์เรย์ 2 มิติไปยังฟังก์ชันนี้ได้อย่างถูกกฎหมาย C-FAQ ประณามการแก้ปัญหาตามปกติของการทำint x[5][10]; process_pointer_2_pointer((int**)&x[0][0], 5, 10);
เพราะอาจนำไปสู่พฤติกรรมที่ไม่ได้กำหนดเนื่องจากการแบนอาร์เรย์ วิธีที่ถูกต้องในการส่งอาเรย์ในวิธีนี้นำเราไปสู่ส่วนที่ไม่สะดวกนั่นคือเราต้องการอาเรย์ (ตัวแทน) เพิ่มเติมของพอยน์เตอร์โดยแต่ละองค์ประกอบจะชี้ไปยังแถวตามลำดับของอาเรย์ที่ถูกส่งผ่านจริง ตัวแทนนี้จะถูกส่งผ่านไปยังฟังก์ชั่น (ดูด้านล่าง); ทั้งหมดนี้เพื่อให้ได้งานที่เหมือนกันกับวิธีการข้างต้นซึ่งปลอดภัยกว่าสะอาดกว่าและเร็วกว่า
นี่คือโปรแกรมควบคุมเพื่อทดสอบฟังก์ชั่นด้านบน:
#include <iostream>
// copy above functions here
int main()
{
int a[5][10] = { { } };
process_2d_array_template(a);
process_2d_array_pointer(&a); // <-- notice the unusual usage of addressof (&) operator on an array
process_2d_array(a, 5);
// works since a's first dimension decays into a pointer thereby becoming int (*)[10]
int *b[5]; // surrogate
for (size_t i = 0; i < 5; ++i)
{
b[i] = a[i];
}
// another popular way to define b: here the 2D arrays dims may be non-const, runtime var
// int **b = new int*[5];
// for (size_t i = 0; i < 5; ++i) b[i] = new int[10];
process_pointer_2_pointer(b, 5, 10);
// process_2d_array(b, 5);
// doesn't work since b's first dimension decays into a pointer thereby becoming int**
}