在函数中返回数组
我有一个传递给函数fillarr(int arr[])
的数组int arr[5]
:
int fillarr(int arr[]) { for(...); return arr; }
- 我怎样才能返回数组?
- 我将如何使用它,说我回来了一个指针,我怎么去访问它?
在这种情况下,你的数组变量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 ++不允许将整个数组作为参数返回给函数。 但是,您可以通过指定数组的名称而不使用索引来返回指向数组的指针。
- 如果你想从一个函数返回一维数组,你将不得不声明一个返回指针的函数,如下例所示:
int * myFunction() { . . . }
- 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::vector
或std::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)非常相似,但是在这种情况下它们不起作用。