นี่เป็นคำถามที่ค่อนข้างเก่า แต่ฉันจะใส่ใน 2 เซ็นต์ของฉันเนื่องจากมีคำตอบมากมาย แต่ไม่มีใครแสดงวิธีการที่เป็นไปได้ทั้งหมดในลักษณะที่ชัดเจนและรัดกุม (ไม่แน่ใจเกี่ยวกับบิตรัดกุมเนื่องจากมี บิตออกจากมือ TL; DR 😉)
ฉันสมมติว่า OP ต้องการคืนค่าอาร์เรย์ที่ส่งผ่านโดยไม่คัดลอกเป็นวิธีการส่งผ่านโดยตรงไปยังผู้เรียกเพื่อส่งผ่านไปยังฟังก์ชันอื่นเพื่อให้โค้ดดูน่าสนใจยิ่งขึ้น
อย่างไรก็ตามเพื่อให้การใช้อาร์เรย์เช่นนี้คือการปล่อยให้มันสลายตัวลงไปในตัวชี้และมีการรักษาเรียบเรียงมันชอบอาร์เรย์ สิ่งนี้อาจส่งผลให้เกิดข้อผิดพลาดที่ละเอียดอ่อนหากคุณส่งผ่านอาร์เรย์เช่นเดียวกันกับฟังก์ชั่นที่คาดหวังว่ามันจะมี 5 องค์ประกอบ แต่โทรของคุณจริง ๆ ผ่านหมายเลขอื่น
มีสองสามวิธีที่คุณสามารถจัดการสิ่งนี้ได้ดีกว่า ผ่านในstd::vector
หรือstd::array
(ไม่แน่ใจว่าstd::array
อยู่ในปี 2010 เมื่อมีคำถามหรือไม่) จากนั้นคุณสามารถส่งผ่านวัตถุเป็นข้อมูลอ้างอิงได้โดยไม่ต้องคัดลอกหรือย้ายวัตถุ
std::array<int, 5>& fillarr(std::array<int, 5>& arr)
{
// (before c++11)
for(auto it = arr.begin(); it != arr.end(); ++it)
{ /* do stuff */ }
// Note the following are for c++11 and higher. They will work for all
// the other examples below except for the stuff after the Edit.
// (c++11 and up)
for(auto it = std::begin(arr); it != std::end(arr); ++it)
{ /* do stuff */ }
// range for loop (c++11 and up)
for(auto& element : arr)
{ /* do stuff */ }
return arr;
}
std::vector<int>& fillarr(std::vector<int>& arr)
{
for(auto it = arr.begin(); it != arr.end(); ++it)
{ /* do stuff */ }
return arr;
}
อย่างไรก็ตามหากคุณยืนยันที่จะเล่นกับอาร์เรย์ C ให้ใช้เทมเพลตซึ่งจะเก็บข้อมูลจำนวนไอเท็มในอาร์เรย์
template <size_t N>
int(&fillarr(int(&arr)[N]))[N]
{
// N is easier and cleaner than specifying sizeof(arr)/sizeof(arr[0])
for(int* it = arr; it != arr + N; ++it)
{ /* do stuff */ }
return arr;
}
ยกเว้นว่าหน้าตาน่าเกลียดและอ่านยากสุด ๆ ตอนนี้ฉันใช้บางสิ่งบางอย่างเพื่อช่วยเหลือสิ่งที่ไม่ได้อยู่ในปี 2010 ซึ่งฉันใช้สำหรับพอยน์เตอร์ของฟังก์ชัน:
template <typename T>
using type_t = T;
template <size_t N>
type_t<int(&)[N]> fillarr(type_t<int(&)[N]> arr)
{
// N is easier and cleaner than specifying sizeof(arr)/sizeof(arr[0])
for(int* it = arr; it != arr + N; ++it)
{ /* do stuff */ }
return arr;
}
นี้ย้ายชนิดที่ใครจะคาดคิดว่ามันจะทำให้นี้ไกลอ่านได้มากขึ้น แน่นอนว่าการใช้เทมเพลตนั้นไม่จำเป็นหากคุณไม่ได้ใช้อะไรเลยนอกจากองค์ประกอบ 5 อย่างดังนั้นคุณจึงสามารถใช้รหัสยากได้:
type_t<int(&)[5]> fillarr(type_t<int(&)[5]> arr)
{
// Prefer using the compiler to figure out how many elements there are
// as it reduces the number of locations where you have to change if needed.
for(int* it = arr; it != arr + sizeof(arr)/sizeof(arr[0]); ++it)
{ /* do stuff */ }
return arr;
}
ดังที่ฉันพูดtype_t<>
กลอุบายของฉันจะไม่ทำงานในเวลาที่ถามคำถามนี้ สิ่งที่ดีที่สุดที่คุณคาดหวังหลังจากนั้นก็คือใช้ประเภทในโครงสร้าง:
template<typename T>
struct type
{
typedef T type;
};
typename type<int(&)[5]>::type fillarr(typename type<int(&)[5]>::type arr)
{
// Prefer using the compiler to figure out how many elements there are
// as it reduces the number of locations where you have to change if needed.
for(int* it = arr; it != arr + sizeof(arr)/sizeof(arr[0]); ++it)
{ /* do stuff */ }
return arr;
}
ซึ่งเริ่มดูน่าเกลียดอีกครั้ง แต่อย่างน้อยก็ยังสามารถอ่านได้มากกว่าแม้ว่าtypename
อาจจะมีตัวเลือกกลับมาแล้วขึ้นอยู่กับคอมไพเลอร์ส่งผลให้:
type<int(&)[5]>::type fillarr(type<int(&)[5]>::type arr)
{
// Prefer using the compiler to figure out how many elements there are
// as it reduces the number of locations where you have to change if needed.
for(int* it = arr; it != arr + sizeof(arr)/sizeof(arr[0]); ++it)
{ /* do stuff */ }
return arr;
}
และแน่นอนคุณสามารถระบุประเภทเฉพาะได้แทนที่จะใช้ตัวช่วยของฉัน
typedef int(&array5)[5];
array5 fillarr(array5 arr)
{
// Prefer using the compiler to figure out how many elements there are
// as it reduces the number of locations where you have to change if needed.
for(int* it = arr; it != arr + sizeof(arr)/sizeof(arr[0]); ++it)
{ /* do stuff */ }
return arr;
}
ย้อนกลับไปตอนนี้ฟังก์ชั่นฟรีstd::begin()
และstd::end()
ไม่มีอยู่แม้ว่าสามารถนำไปใช้ได้อย่างง่ายดาย สิ่งนี้จะอนุญาตให้วนซ้ำอาร์เรย์ในลักษณะที่ปลอดภัยกว่าเนื่องจากมีความเหมาะสมกับอาร์เรย์ C แต่ไม่ใช่ตัวชี้
สำหรับการเข้าถึงอาร์เรย์คุณสามารถส่งผ่านไปยังฟังก์ชันอื่นที่ใช้พารามิเตอร์ชนิดเดียวกันหรือสร้างนามแฝงกับมัน (ซึ่งจะไม่สมเหตุสมผลเท่าที่คุณมีต้นฉบับในขอบเขตนั้น) การเข้าถึงการอ้างอิงอาร์เรย์เหมือนกับการเข้าถึงอาร์เรย์ดั้งเดิม
void other_function(type_t<int(&)[5]> x) { /* do something else */ }
void fn()
{
int array[5];
other_function(fillarr(array));
}
หรือ
void fn()
{
int array[5];
auto& array2 = fillarr(array); // alias. But why bother.
int forth_entry = array[4];
int forth_entry2 = array2[4]; // same value as forth_entry
}
เพื่อสรุปว่าเป็นการดีที่สุดที่จะไม่ยอมให้อาร์เรย์สลายตัวเป็นพอยน์เตอร์ถ้าคุณตั้งใจจะย้ำให้มัน มันเป็นความคิดที่ไม่ดีเพราะมันทำให้คอมไพเลอร์ปกป้องคุณจากการยิงตัวเองในเชิงเท้าและทำให้โค้ดของคุณอ่านยากขึ้น พยายามและคอมไพเลอร์ช่วยคุณโดยรักษาประเภทให้นานที่สุดเว้นแต่คุณจะมีเหตุผลที่ดีที่จะไม่ทำเช่นนั้น
แก้ไข
โอ้และเพื่อความสมบูรณ์คุณสามารถอนุญาตให้ลดระดับลงไปที่ตัวชี้ แต่สิ่งนี้จะแยกอาร์เรย์ออกจากจำนวนองค์ประกอบที่มีอยู่ สิ่งนี้กระทำได้มากมายใน C / C ++ และมักจะลดลงโดยการผ่านจำนวนองค์ประกอบในอาร์เรย์ อย่างไรก็ตามคอมไพเลอร์ไม่สามารถช่วยคุณได้หากคุณทำผิดพลาดและส่งผ่านค่าผิดไปยังจำนวนองค์ประกอบ
// separate size value
int* fillarr(int* arr, size_t size)
{
for(int* it = arr; it != arr + size; ++it)
{ /* do stuff */ }
return arr;
}
แทนที่จะผ่านขนาดคุณสามารถผ่านตัวชี้สิ้นสุดซึ่งจะชี้ไปที่จุดสิ้นสุดของอาร์เรย์ของคุณ สิ่งนี้มีประโยชน์เพราะมันทำให้บางสิ่งที่ใกล้กับอัลกอริธึม std ซึ่งใช้ตัวเริ่มต้นและตัวชี้ปลาย แต่สิ่งที่คุณกลับมาตอนนี้เป็นสิ่งที่คุณต้องจำเท่านั้น
// separate end pointer
int* fillarr(int* arr, int* end)
{
for(int* it = arr; it != end; ++it)
{ /* do stuff */ }
return arr;
}
หรือคุณสามารถบันทึกว่าฟังก์ชั่นนี้ใช้เวลาเพียง 5 องค์ประกอบและหวังว่าผู้ใช้ฟังก์ชั่นของคุณจะไม่ทำอะไรโง่ ๆ
// I document that this function will ONLY take 5 elements and
// return the same array of 5 elements. If you pass in anything
// else, may nazal demons exit thine nose!
int* fillarr(int* arr)
{
for(int* it = arr; it != arr + 5; ++it)
{ /* do stuff */ }
return arr;
}
โปรดทราบว่าค่าที่ส่งคืนจะหายไปเป็นประเภทดั้งเดิมและลดระดับลงเป็นตัวชี้ ด้วยเหตุนี้คุณจึงต้องมั่นใจว่าคุณจะไม่บุกรุกอาร์เรย์
คุณสามารถส่งผ่านstd::pair<int*, int*>
ซึ่งคุณสามารถใช้สำหรับการเริ่มต้นและสิ้นสุดและผ่านรอบ ๆ แต่จริงๆแล้วมันหยุดดูเหมือนอาร์เรย์
std::pair<int*, int*> fillarr(std::pair<int*, int*> arr)
{
for(int* it = arr.first; it != arr.second; ++it)
{ /* do stuff */ }
return arr; // if you change arr, then return the original arr value.
}
void fn()
{
int array[5];
auto array2 = fillarr(std::make_pair(&array[0], &array[5]));
// Can be done, but you have the original array in scope, so why bother.
int fourth_element = array2.first[4];
}
หรือ
void other_function(std::pair<int*, int*> array)
{
// Can be done, but you have the original array in scope, so why bother.
int fourth_element = array2.first[4];
}
void fn()
{
int array[5];
other_function(fillarr(std::make_pair(&array[0], &array[5])));
}
ตลกดีนี่มันคล้ายกับวิธีการstd::initializer_list
ทำงาน (c ++ 11) แต่มันไม่ทำงานในบริบทนี้