在函数中返回数组

我有一个传递给函数fillarr(int arr[])的数组int arr[5]

 int fillarr(int arr[]) { for(...); return arr; } 
  1. 我怎样才能返回数组?
  2. 我将如何使用它,说我回来了一个指针,我怎么去访问它?

在这种情况下,你的数组变量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; } 

C ++函数不能按值返回C风格的数组。 最接近的是返回一个指针。 此外,参数列表中的数组类型只是简单地转换为指针。

 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,传递引用只是可选的,而语法则不那么令人费解:

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

array模板只是生成一个包含C风格数组的结构,所以您可以应用面向对象的语义,但保留数组的原始简单性。

$ 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); } 

答案可能取决于你打算如何使用该功能。 对于最简单的答案,让我们决定,而不是一个数组,你真正想要的是一个向量。 向量是很好的,因为所有的世界看起来像无聊,普通的值,你可以存储在常规指针。 我们会看看其他选项,以及之后为什么要这样做:

 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关键字来修改参数列表。 现在你得到一个引用而不复制它,但是你不能修改它,所以调用者知道它将和函数到达之前一样。

所有这些都是膨胀的,但惯用的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); 

现在看起来很像这个问题其他地方提供的简单指针示例。

在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; } 

这个:

 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; } 

我建议你可以考虑把这个长度放到你的fillarr函数中。

 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

从一个函数返回一个数组,让我们在一个结构中定义该数组; 所以它看起来像这样

 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; } 

最简单的方法是通过引用来返回它,即使你不写'&'符号,它也是通过引用自动返回的

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

你仍然可以使用结果

 int *returned_array = fillarr(some_other_array); if(returned_array[0] == 3) do_important_cool_stuff(); 
 template<typename T, size_t N> using ARR_REF = T (&)[N]; template <typename T, size_t N> ARR_REF<T,N> ArraySizeHelper(ARR_REF<T,N> arr); #define arraysize(arr) sizeof(ArraySizeHelper(arr)) 

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 

只需定义一个类型[]作为返回值,如:

  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) 

这是一个相当古老的问题,但由于答案很多,所以我会把我的2分钱,但没有一个以清晰简洁的方式显示所有可能的方法(不确定简单的位,因为这有一个有点失控。TL; DR😉)。

我假设OP想要返回传入的数组而不进行复制,直接将其传递给调用者以传递给另一个函数,以使代码看起来更漂亮。

然而,像这样使用数组是为了让它衰变成一个指针,让编译器把它看作一个数组。 这可能会导致微妙的错误,如果你传递一个数组,如果函数期望它有5个元素,但你的调用者实际上传递了一些其他的数字。

有几种方法可以更好地处理这个问题。 传入一个std::vectorstd::array (不知道是否在2010年时问std::array问)。 然后您可以传递该对象作为参考,而不需要复制/移动该对象。

 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)非常相似,但是在这种情况下它们不起作用。