ส่งคืนอาร์เรย์ในฟังก์ชัน


212

ฉันมีอาร์เรย์int arr[5]ที่ส่งผ่านไปยังฟังก์ชันfillarr(int arr[]):

int fillarr(int arr[])
{
    for(...);
    return arr;
}
  1. ฉันจะคืนค่าอาร์เรย์นั้นได้อย่างไร
  2. ฉันจะใช้มันอย่างไรบอกว่าฉันคืนพอยน์เตอร์ให้ฉันจะเข้าถึงได้อย่างไร?

46
การพูดอย่างเคร่งครัดในบริบทนี้คุณไม่จำเป็นต้องส่งคืนอาร์เรย์เนื่องจากอาร์เรย์ถูกส่งผ่านโดยการอ้างอิงดังนั้นการเปลี่ยนแปลงองค์ประกอบใด ๆ ภายใน 'arr' จะเห็นนอกฟังก์ชัน
BuggerMe

12
การคืนอาเรย์จะสะดวกสำหรับฟังก์ชั่นผูกมัด
seand

5
ตราบใดที่คุณไม่ได้ทำผิดพลาดในการสร้างอาร์เรย์บนสแต็กและส่งกลับตัวชี้ไปที่มัน
Detly

2
@ismail: มันไม่สามารถส่งคืนอาร์เรย์ใหม่เว้นแต่ว่าอาร์เรย์นั้นได้รับการจัดสรรแบบไดนามิก std::vectorและหากเป็นกรณีที่ใช้
GManNickG

4
@BuggerMe: อาร์เรย์จะไม่ถูกส่งผ่านโดยการอ้างอิง (เว้นแต่คุณจะร้องขอด้วยไวยากรณ์ที่สนุกกว่า) ในรหัสอาร์เรย์จะสลายตัวเป็นตัวชี้ไปยังองค์ประกอบแรกและส่งผ่านไปยังฟังก์ชัน 5ในลายเซ็นของฟังก์ชั่นจะถูกยกเลิกโดยรวบรวม
David Rodríguez - dribeas

คำตอบ:


204

ในกรณีนี้ตัวแปรอาร์เรย์ของคุณarrสามารถใช้เป็นตัวชี้ไปยังจุดเริ่มต้นของบล็อกอาร์เรย์ในหน่วยความจำได้โดยการแปลงโดยนัย ไวยากรณ์ที่คุณใช้:

int fillarr(int arr[])

เป็นน้ำตาลซินแทคติคชนิดหนึ่ง คุณสามารถแทนที่ด้วยสิ่งนี้และมันจะยังคงทำงาน:

int fillarr(int* arr)

ดังนั้นในแง่เดียวกันสิ่งที่คุณต้องการกลับมาจากฟังก์ชั่นของคุณคือตัวชี้ไปยังองค์ประกอบแรกในอาร์เรย์:

int* fillarr(int arr[])

และคุณจะยังสามารถใช้งานได้เหมือนกับที่คุณใช้กับอาร์เรย์ปกติ:

int main()
{
  int y[10];
  int *a = fillarr(y);
  cout << a[0] << endl;
}

45
เพื่อชี้แจงว่า "คำสั่งคลาสสิก C ++" เป็นเท็จ; อาร์เรย์ไม่ใช่พอยน์เตอร์
GManNickG

25
จำกฎ [i] == * (a + i)
seand

8
@Brent Nash ไม่ อาร์เรย์คืออาร์เรย์ ตัวชี้ไปยังจุดเริ่มต้นของอาร์เรย์คือตัวชี้ มันเพิ่งเกิดขึ้นที่คอมไพเลอร์มีน้ำตาลประโยคที่แปลให้คุณในบางสถานการณ์ arrayและ&arrayสามารถใช้แทนกันได้ในหลายกรณี
Carl Norum

20
@Brent: ไม่อาร์เรย์เป็นประเภทของตัวเองไม่ใช่ตัวชี้ชนิดพิเศษ ประเภทของaในint a[10]int[10]เป็น สิ่งที่คุณสามารถพูดได้คืออาร์เรย์ "สลาย" เป็นตัวชี้ไปยังองค์ประกอบแรกของพวกเขา (นี่เป็นการแปลงแบบอาเรย์ต่อตัวชี้โดยปริยาย) จากนั้นคำตอบของคุณจะเป็นไปตามที่ฉันสั่ง หากคุณแก้ไขคำตอบของคุณเพื่อแยกความแตกต่างระหว่างอาร์เรย์การแปลงอาเรย์เป็นตัวชี้และพอยน์เตอร์ฉันจะลบคำตอบของฉันเพราะพวกเขาจะมีข้อมูลหลักที่เหมือนกันและคุณเป็นคนแรก
GManNickG

8
@seand จดจำกฎ [i] == * (a + sizeof (a) * i)
Amir

114

ฟังก์ชั่น C ++ ไม่สามารถส่งคืนอาร์เรย์ C-style ตามค่า สิ่งที่ใกล้เคียงที่สุดคือการส่งคืนพอยน์เตอร์ นอกจากนี้ประเภทอาร์เรย์ในรายการอาร์กิวเมนต์จะถูกแปลงเป็นตัวชี้

int *fillarr( int arr[] ) { // arr "decays" to type int *
    return arr;
}

คุณสามารถปรับปรุงได้โดยใช้การอ้างอิงอาร์เรย์สำหรับอาร์กิวเมนต์และส่งคืนซึ่งป้องกันการสลายตัว:

int ( &fillarr( int (&arr)[5] ) )[5] { // no decay; argument must be size 5
    return arr;
}

ด้วย Boost หรือ C ++ 11 การอ้างอิงแบบ pass-by-by เป็นทางเลือกเพียงอย่างเดียวและไวยากรณ์น้อยกว่าการดัด:

array< int, 5 > &fillarr( array< int, 5 > &arr ) {
    return arr; // "array" being boost::array or std::array
}

arrayแม่แบบเพียงแค่สร้างstructที่มีอาร์เรย์แบบ C เพื่อให้คุณสามารถนำไปใช้ในความหมายเชิงวัตถุยังเก็บอาร์เรย์ของความเรียบง่ายเดิม


4
+1 ที่ให้ตัวอย่างของวิธีการที่อาร์เรย์สามารถส่งผ่านโดยการอ้างอิง แต่คุณผิดที่คุณไม่สามารถคืนค่าอาร์เรย์โดยอ้างอิงได้ ไวยากรณ์ที่ง่ายที่สุดเพื่อให้บรรลุมันคือการใช้ typedef A: typedef int array[5]; array& foo();แต่คุณไม่จำเป็นต้อง typedef ถ้าคุณสนใจที่จะเขียนนี้: ตัวอย่างในคำถามจะเป็น:int (&foo())[5] { static int a[5] = {}; return a; } int (&foo( int (&a)[5] ))[5] { return a; }ง่ายใช่มั้ย
David Rodríguez - dribeas

@ David: ขอบคุณฉันได้รับความผิดพลาดจากข้อความ Comeau error: function returning array is not allowedซึ่งเกิดขึ้นหากคุณออกจาก parens ด้านนอกในไวยากรณ์ที่ไม่ใช่ typedef โชคดีที่วันนี้ฉันตรวจสอบกฎด้านซ้ายสำหรับคำถามอื่นและจัดการเพื่อสร้างสิ่งที่ถูกต้อง ... หลังจากเห็นคุณพูดว่าเป็นไปได้…ก่อนที่จะเห็นว่าคุณให้รหัส: vP
Potatoswatter

1
คำตอบโดยchubsdadมีเครื่องหมายคำพูดที่ถูกต้องจากมาตรฐาน: คุณไม่สามารถส่งคืนอาร์เรย์ แต่คุณสามารถส่งกลับการอ้างอิงหรือตัวชี้ไปยังอาร์เรย์ อาร์เรย์ไม่สามารถคัดลอกได้ (เป็นประเภท) และไม่สามารถส่งคืนได้ซึ่งจะหมายถึงการคัดลอก - และเมื่อไวยากรณ์นั้นมีอยู่คอมไพเลอร์จะแปลงอาร์กิวเมนต์เป็นตัวชี้
David Rodríguez - dribeas

3
@ David: ดังนั้น หน้านี้มีความยาวพิลึก ไม่เคยมีคนจำนวนมากที่เขียนฟังก์ชั่นเล็ก ๆ น้อย ๆ โดยสมัครใจกลับอาร์เรย์ในที่เดียว
Potatoswatter

@Potatoswatter ฉันยังใหม่กับ cpp คุณสามารถอธิบายรายละเอียดของโค้ดที่ 2 ได้หรือไม่ ฉันไม่สามารถแยกมันออกเป็นส่วน ๆ เพื่อความเข้าใจ
KPMG

23

ใน C ++ 11 คุณสามารถส่งคืนstd::arrayได้

#include <array>
using namespace std;

array<int, 5> fillarr(int arr[])
{
    array<int, 5> arr2;
    for(int i=0; i<5; ++i) {
        arr2[i]=arr[i]*2;
    }
    return arr2;
}

2
Quoting OP:(...) you can consider the array returned arr2, totally another array (...)
cubuspl42

22

$ 8.3.5 / 8 สถานะ -

"ฟังก์ชั่นจะต้องไม่มีประเภทส่งคืนของอาเรย์ประเภทหรือฟังก์ชั่นแม้ว่าพวกเขาอาจจะมีประเภทกลับมาของตัวชี้ประเภทหรือการอ้างอิงถึงสิ่งดังกล่าวจะต้องไม่มีฟังก์ชั่นอาร์เรย์แม้ว่าจะมี

int (&fn1(int (&arr)[5]))[5]{     // declare fn1 as returning refernce to array
   return arr;
}

int *fn2(int arr[]){              // declare fn2 as returning pointer to array
   return arr;
}


int main(){
   int buf[5];
   fn1(buf);
   fn2(buf);
}

7
ฟังก์ชันที่สองของคุณคืนค่าพอยน์เตอร์ไปที่intไม่ใช่อาร์เรย์
GManNickG

อีกครั้งทำไมส่งคืนชนิดเมื่อมีการปรับปรุงอาร์เรย์จริงภายในฟังก์ชัน มันเป็นเรื่องของการปฏิบัติที่ดีที่สุดหรือไม่?
Dan

14

คำตอบอาจขึ้นอยู่กับว่าคุณวางแผนจะใช้ฟังก์ชันนั้นอย่างไร สำหรับคำตอบที่ง่ายที่สุดให้ตัดสินใจว่าแทนที่จะเป็นอาร์เรย์สิ่งที่คุณต้องการคือเวกเตอร์ เวกเตอร์เป็นสิ่งที่ดีเพราะมองหาคนทั้งโลกอย่างน่าเบื่อค่านิยมทั่วไปที่คุณสามารถเก็บไว้ในพอยน์เตอร์ปกติ เราจะดูตัวเลือกอื่น ๆ และเหตุผลที่คุณต้องการในภายหลัง:

std::vector<int> fillarr( std::vector<int> arr ) {
    // do something
    return arr;
}

นี่จะทำสิ่งที่คุณคาดหวัง ข้อเสียคือstd::vectorการดูแลให้แน่ใจว่าทุกอย่างได้รับการจัดการอย่างหมดจด ข้อเสียคือสิ่งนี้คัดลอกข้อมูลจำนวนมากถ้าอาร์เรย์ของคุณมีขนาดใหญ่ ในความเป็นจริงมันจะคัดลอกทุกองค์ประกอบของอาร์เรย์สองครั้ง ก่อนอื่นให้คัดลอกเวกเตอร์เพื่อให้ฟังก์ชันสามารถใช้เป็นพารามิเตอร์ได้ จากนั้นจะคัดลอกอีกครั้งเพื่อส่งคืนไปยังผู้โทร หากคุณสามารถจัดการกับเวกเตอร์ด้วยตัวเองคุณสามารถทำสิ่งต่าง ๆ ได้ง่ายขึ้น (มันอาจจะคัดลอกมันเป็นครั้งที่สามถ้าผู้เรียกจำเป็นต้องเก็บไว้ในตัวแปรของการเรียงลำดับบางอย่างเพื่อทำการคำนวณเพิ่มเติม)

ดูเหมือนว่าสิ่งที่คุณพยายามจะทำจริงๆก็แค่เติมคอลเล็กชัน หากคุณไม่มีเหตุผลที่เฉพาะเจาะจงในการส่งคืนอินสแตนซ์ใหม่ของคอลเลกชันแสดงว่าไม่มี เราทำได้เช่นนี้

void fillarr(std::vector<int> &  arr) {
    // modify arr
    // don't return anything
}

วิธีนี้คุณจะได้รับการอ้างอิงถึงอาร์เรย์ที่ส่งผ่านไปยังฟังก์ชันไม่ใช่สำเนาส่วนตัวของมัน การเปลี่ยนแปลงใด ๆ ที่คุณทำกับพารามิเตอร์จะถูกเรียกโดยผู้เรียก คุณสามารถส่งคืนการอ้างอิงได้หากคุณต้องการ แต่นั่นไม่ใช่ความคิดที่ดีจริงๆเพราะมันหมายถึงว่าคุณได้รับสิ่งที่แตกต่างจากสิ่งที่คุณผ่าน

หากคุณต้องการอินสแตนซ์ใหม่ของคอลเลกชันจริง ๆ แต่ต้องการหลีกเลี่ยงมันบนสแต็ก (และการคัดลอกทั้งหมดที่เกี่ยวข้อง) คุณต้องสร้างสัญญาบางประเภทสำหรับวิธีจัดการอินสแตนซ์นั้น วิธีที่ง่ายที่สุดในการทำเช่นนั้นคือการใช้ตัวชี้สมาร์ทซึ่งเก็บอินสแตนซ์ที่อ้างอิงไว้รอบตัวตราบเท่าที่ทุกคนกำลังถืออยู่ มันจะหายไปอย่างหมดจดหากหลุดออกจากขอบเขต ที่จะมีลักษณะเช่นนี้

std::auto_ptr<std::vector<int> > fillarr( const std::vector<int> & arr) {
    std::auto_ptr<std::vector<int> > myArr(new std::vector<int>);
    // do stuff with arr and *myArr
    return myArr;
}

ส่วนใหญ่การใช้*myArrผลงานเหมือนกันกับการใช้เวกเตอร์วานิลลาธรรมดา ตัวอย่างนี้ยังแก้ไขรายการพารามิเตอร์โดยเพิ่มconstคำหลัก ตอนนี้คุณจะได้รับการอ้างอิงโดยไม่คัดลอก แต่คุณไม่สามารถแก้ไขได้ดังนั้นผู้โทรจึงรู้ว่ามันจะเหมือนกับก่อนหน้าที่จะใช้งาน

ทั้งหมดนี้เป็นอาการบวม แต่ idiomatic c ++ นั้นไม่ค่อยได้ผลกับคอลเลกชันโดยรวม โดยปกติคุณจะใช้ตัววนซ้ำกับคอลเล็กชันเหล่านั้น ที่จะมีลักษณะเช่นนี้

template <class Iterator>
Iterator fillarr(Iterator arrStart, Iterator arrEnd) {
    Iterator arrIter = arrStart;
    for(;arrIter <= arrEnd; arrIter++)
       ;// do something
    return arrStart;
}

การใช้มันดูแปลก ๆ ถ้าคุณไม่คุ้นเคยกับสไตล์นี้

vector<int> arr;
vector<int>::iterator foo = fillarr(arr.begin(), arr.end());

foo นี้ 'ชี้ไปที่' arrจุดเริ่มต้นของการปรับเปลี่ยน

สิ่งที่ดีเกี่ยวกับเรื่องนี้ก็คือมันทำงานได้ดีเท่าเทียมกันบนเวกเตอร์เช่นเดียวกับในอาร์เรย์ C ธรรมดาและคอลเลกชันประเภทอื่น ๆ อีกมากมาย

int arr[100];
int *foo = fillarr(arr, arr+100);

ซึ่งตอนนี้ดูเหมือนล็อตแย่มากอย่างเช่นตัวชี้ธรรมดาที่ให้ไว้ที่อื่นในคำถามนี้


ไวยากรณ์ไม่ถูก&ต้องสัญลักษณ์จะต้องปรากฏตามประเภท:void fillarr(std::vector<int> & arr)
David Rodríguez - dribeas

9

นี้:

int fillarr(int arr[])

ได้รับการปฏิบัติเช่นเดียวกับ:

int fillarr(int *arr)

ตอนนี้ถ้าคุณต้องการคืนค่าอาร์เรย์คุณสามารถเปลี่ยนบรรทัดนั้นเป็น

int * fillarr(int arr[]){
    // do something to arr
    return arr;
}

มันไม่ได้คืนอาร์เรย์จริงๆ คุณกำลังส่งคืนพอยน์เตอร์ไปที่จุดเริ่มต้นของที่อยู่อาร์เรย์

แต่จำไว้ว่าเมื่อคุณผ่านอาร์เรย์คุณจะผ่านตัวชี้เท่านั้น ดังนั้นเมื่อคุณแก้ไขข้อมูลอาร์เรย์คุณจะแก้ไขข้อมูลที่ตัวชี้ชี้ไป ดังนั้นก่อนที่คุณจะส่งผ่านไปยังอาร์เรย์คุณต้องตระหนักว่าคุณมีอยู่นอกผลการแก้ไข

เช่น

int fillarr(int arr[]){
   array[0] = 10;
   array[1] = 5;
}

int main(int argc, char* argv[]){
   int arr[] = { 1,2,3,4,5 };

   // arr[0] == 1
   // arr[1] == 2 etc
   int result = fillarr(arr);
   // arr[0] == 10
   // arr[1] == 5    
   return 0;
}

ฉันขอแนะนำให้คุณอาจต้องการลองใส่ความยาวลงในฟังก์ชั่นเติมของคุณเช่นนี้

int * fillarr(int arr[], int length)

ด้วยวิธีนี้คุณสามารถใช้ความยาวในการเติมอาร์เรย์ให้มีความยาวได้ไม่ว่าจะเป็นอะไรก็ตาม

เพื่อใช้งานได้จริงอย่างถูกต้อง ทำสิ่งนี้:

int * fillarr(int arr[], int length){
   for (int i = 0; i < length; ++i){
      // arr[i] = ? // do what you want to do here
   }
   return arr;
}

// then where you want to use it.
int arr[5];
int *arr2;

arr2 = fillarr(arr, 5);

// at this point, arr & arr2 are basically the same, just slightly
// different types.  You can cast arr to a (char*) and it'll be the same.

หากสิ่งที่คุณต้องการทำคือตั้งค่าอาร์เรย์เป็นค่าเริ่มต้นให้ลองใช้ฟังก์ชัน memset ในตัว

บางอย่างเช่น: memset ((int *) & arr, 5, sizeof (int));

ในขณะที่ฉันอยู่ในหัวข้อแม้ว่า คุณบอกว่าคุณใช้ C ++ ลองดูการใช้เวกเตอร์ stl รหัสของคุณน่าจะแข็งแกร่งกว่านี้

มีแบบฝึกหัดมากมาย นี่คือสิ่งที่ช่วยให้คุณมีความคิดในการใช้งาน http://www.yolinux.com/TUTORIALS/LinuxTutorialC++STL.html


ใช้std::copyมากกว่าmemsetก็ปลอดภัยและง่ายขึ้น (และรวดเร็วถ้าไม่เร็วขึ้น)
GManNickG

5

เมื่อต้องการส่งคืนอาร์เรย์จากฟังก์ชันให้เรากำหนดอาร์เรย์นั้นในโครงสร้าง ดังนั้นมันจึงเป็นแบบนี้

struct Marks{
   int list[5];
}

ตอนนี้ให้เราสร้างตัวแปรของโครงสร้างประเภท

typedef struct Marks marks;
marks marks_list;

เราสามารถส่งอาเรย์ไปยังฟังก์ชันได้ด้วยวิธีต่อไปนี้และกำหนดค่าให้กับมัน:

void setMarks(int marks_array[]){
   for(int i=0;i<sizeof(marks_array)/sizeof(int);i++)
       marks_list.list[i]=marks_array[i];
}

เราสามารถคืนอาเรย์ ในการคืนอาเรย์นั้นประเภทการคืนของฟังก์ชันควรเป็นประเภทโครงสร้างเช่นเครื่องหมาย เนื่องจากในความเป็นจริงเรากำลังผ่านโครงสร้างที่มีอาร์เรย์ ดังนั้นรหัสสุดท้ายอาจมีลักษณะเช่นนี้

marks getMarks(){
 return marks_list;
}

5

นี่เป็นคำถามที่ค่อนข้างเก่า แต่ฉันจะใส่ใน 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) แต่มันไม่ทำงานในบริบทนี้


3

วิธีที่ง่ายที่สุดในการทำเช่นนี้คือการส่งคืนโดยการอ้างอิงแม้ว่าคุณจะไม่ได้เขียนสัญลักษณ์ '&' แต่มันจะถูกส่งกลับโดยอัตโนมัติโดยการอ้างอิง

     void fillarr(int arr[5])
  {
       for(...);

  }

2
int *fillarr(int arr[])

คุณยังสามารถใช้ผลลัพธ์เช่น

int *returned_array = fillarr(some_other_array);
if(returned_array[0] == 3)
    do_important_cool_stuff();

ฉันไม่คิดว่า 'int [] fillarr ... ' ถูกกฎหมาย 'int * fillarr' คือสิ่งที่คุณจะใช้เนื่องจากความเท่าเทียมกันของตัวชี้อาร์เรย์
seand

1

เส้นทางดังกล่าวข้างต้นถูกต้อง แต่ฉันคิดว่าถ้าเราแค่คืนค่าตัวแปรอาร์เรย์ในตัวของฟังก์ชันบางครั้งก็ส่งคืนค่าขยะเป็นองค์ประกอบ

เพื่อหลีกเลี่ยงการที่ฉันต้องสร้างอาร์เรย์แบบไดนามิกและดำเนินการต่อไป ซึ่งเป็นอะไรเช่นนี้

int* func()
{
  int* Arr = new int[100];
  return Arr;
}

int main()
{
  int* ArrResult = func();
  cout << ArrResult[0] << " " << ArrResult[1] << endl;
  return 0;
} 




0

0

ที่มา: https://www.tutorialspoint.com/cplusplus/cpp_return_arrays_from_functions.htm

C ++ ไม่อนุญาตให้ส่งคืนอาร์เรย์ทั้งหมดเป็นอาร์กิวเมนต์ไปยังฟังก์ชัน อย่างไรก็ตามคุณสามารถส่งคืนพอยน์เตอร์ไปยังอาร์เรย์โดยการระบุชื่อของอาเรย์โดยไม่มีดัชนี

  1. หากคุณต้องการส่งคืนอาเรย์แบบมิติเดียวจากฟังก์ชั่นคุณจะต้องประกาศฟังก์ชั่นที่ส่งคืนพอยน์เตอร์ดังตัวอย่างต่อไปนี้:
int * myFunction()    {
   .
   .
   .
}
  1. C ++ ไม่สนับสนุนให้ส่งคืนที่อยู่ของตัวแปรโลคอลไปยังด้านนอกของฟังก์ชั่นดังนั้นคุณจะต้องกำหนดตัวแปรโลคัลเป็นตัวแปรสแตติก

การใช้กฎเหล่านี้กับคำถามปัจจุบันเราสามารถเขียนโปรแกรมดังนี้:

# include <iostream>

using namespace std;

int * fillarr( );


int main ()
{

   int *p;

   p = fillarr();

   for ( int i = 0; i < 5; i++ )
       cout << "p[" << i << "] : "<< *(p + i) << endl;

    return 0;
}


int * fillarr( )
{
    static int  arr[5];

    for (int i = 0; i < 5; ++i)
        arr[i] = i;

    return arr;
 }

ผลลัพธ์จะเป็น:

p[0]=0
p[1]=1
p[2]=2
p[3]=3
p[4]=4

0

และสิ่งที่เกี่ยวกับ:

int (*func())
{
    int *f = new int[10] {1,2,3};

    return f;
}

int fa[10] = { 0 };
auto func2() -> int (*) [10]
{
    return &fa;
}

0

ที่จริงแล้วเมื่อคุณส่งผ่านอาร์เรย์ภายในฟังก์ชั่นตัวชี้ไปยังอาร์เรย์เดิมจะถูกส่งผ่านไปยังพารามิเตอร์ฟังก์ชันและการเปลี่ยนแปลงที่เกิดขึ้นกับอาร์เรย์ภายในฟังก์ชันนั้นจะเกิดขึ้นจริงในอาร์เรย์เดิม

#include <iostream>

using namespace std;

int* func(int ar[])
{
    for(int i=0;i<100;i++) 
        ar[i]=i;
    int *ptr=ar;
    return ptr;
}


int main() {
    int *p;
    int y[100]={0};    
    p=func(y);

    for(int i=0;i<100;i++) 
        cout<<i<<" : "<<y[i]<<'\n';
}

เรียกใช้และคุณจะเห็นการเปลี่ยนแปลง


1
โปรดใช้ถ้อยคำภาษาอังกฤษที่เหมาะสม (คุณจะแทนคุณ) และละเว้นวลีที่ว่างเปล่าเช่น "เพื่อน"
สวัสดี

นอกจากนี้: "แล้วจริงๆแล้วมันถูกส่งผ่านเป็นข้อมูลอ้างอิง" ผิด ตัวแปรyจะถูกส่งเป็นสำเนาของตัวเอง แต่เนื่องจากเป็นตัวชี้คุณจะทำงานบนอาเรย์โดยตรง โปรดแก้ไขคำตอบของคุณ
สวัสดี

stackoverflow.com/questions/5573310/… TL; DR "ดังนั้นทั้งสองรูปแบบจึงเหมือนกัน"
สวัสดี

ใช่มันเป็นเทคนิคในแบบอาเรย์คุณพูดถูก แต่สิ่งที่คัดลอกมานั้นเป็นตัวชี้ไปยังอาเรย์ไม่ใช่ตัวอาเรย์เอง
สวัสดี

0

นี่คือตัวอย่างเต็มของปัญหาแบบนี้ที่จะแก้ไข

#include <bits/stdc++.h>
using namespace std;
int* solve(int brr[],int n)
{
sort(brr,brr+n);
return brr;
}

int main()
{
int n;
cin>>n;
int arr[n];
for(int i=0;i<n;i++)
{
    cin>>arr[i];
}
int *a=solve(arr,n);
for(int i=0;i<n;i++)
{
    cout<<a[i]<<endl;
}

return 0;
}

-2

เพียงแค่กำหนดประเภท [] เป็นค่าตอบแทนเช่น:

        private string[] functionReturnValueArray(string one, string two)
    {

        string[] x = {one, two};


        x[0] = "a";
        x[1] = "b";

        return x;
    }

. . . ฟังก์ชั่นการโทร:

string[] y;
y = functionReturnValueArray(stringOne, stringTwo)

5
นี่ไม่ใช่ C ++
Adrian
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.