我有一个传递给函数 fillarr(int arr[])
的数组 int arr[5]
:
int fillarr(int arr[])
{
for(...);
return arr;
}
我怎样才能返回该数组?我将如何使用它,比如我返回了一个指针,我将如何访问它?
5
被编译器丢弃。
void foo( int (&array)[5] );
(按引用传递 5 个整数的数组)。当您通过引用传递时,您在函数中得到的是对实际类型的 reference。另一方面,void foo( int array[5] )
在函数定义期间由编译器翻译为 void foo(int*)
。调用 foo( myarray )
会产生数组的 decay 指向第一个元素的指针。
在这种情况下,您的数组变量 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 样式数组的 struct
,因此您可以应用面向对象的语义,同时保留数组的原始简单性。
typedef int array[5]; array& foo();
但如果您想写这个,您甚至不需要 typedef:int (&foo())[5] { static int a[5] = {}; return a; }
,问题中的示例是:int (&foo( int (&a)[5] ))[5] { return a; }
。很简单,不是吗?
error: function returning array is not allowed
的错误印象。幸运的是,今天我检查了另一个问题的左右规则并设法构造了正确的东西......在看到你说有可能......在看到你给出代码之前......
在 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;
}
(...) you can consider the array returned arr2, totally another array (...)
$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);
}
int
的指针,而不是数组。
答案可能取决于您打算如何使用该功能。对于最简单的答案,让我们决定你真正想要的不是一个数组,而是一个向量。向量很好,因为它看起来像无聊的普通值一样可以存储在常规指针中。之后我们将研究其他选项以及您想要它们的原因:
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
的工作方式与使用普通的 vanilla 向量相同。此示例还通过添加 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);
现在看起来很像这个问题中其他地方给出的普通指针示例。
&
符号必须出现在类型之后:void fillarr(std::vector<int> & arr)
这个:
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
std::copy
而不是 memset
,它更安全、更容易。 (即使不是更快,也一样快。)
这是一个相当古老的问题,但我将投入 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*>
,您可以将它用于 begin 和 end 并传递它,但它真的不再看起来像一个数组。
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) 非常相似,但它们在这种情况下不起作用。
要从函数返回数组,让我们在结构中定义该数组;所以它看起来像这样
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();
如上所述路径是正确的。但我认为如果我们只返回一个函数的局部数组变量,有时它会返回垃圾值作为它的元素。
为了避免我不得不动态创建数组并继续。这是这样的。
int* func()
{
int* Arr = new int[100];
return Arr;
}
int main()
{
int* ArrResult = func();
cout << ArrResult[0] << " " << ArrResult[1] << endl;
return 0;
}
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))
来源:https://www.tutorialspoint.com/cplusplus/cpp_return_arrays_from_functions.htm
C++ 不允许将整个数组作为参数返回给函数。但是,您可以通过指定不带索引的数组名称来返回指向数组的指针。
如果要从函数返回一维数组,则必须声明一个返回指针的函数,如下例所示:
int * myFunction() { . . . }
C++ 不提倡将局部变量的地址返回到函数外部,因此您必须将局部变量定义为静态变量。
将这些规则应用于当前问题,我们可以编写如下程序:
# 使用命名空间标准包含
输出将是:
p[0]=0
p[1]=1
p[2]=2
p[3]=3
p[4]=4
那么:
int (*func())
{
int *f = new int[10] {1,2,3};
return f;
}
int fa[10] = { 0 };
auto func2() -> int (*) [10]
{
return &fa;
}
实际上,当您在函数内部传递数组时,指向原始数组的指针会在函数参数中传递,因此对该函数内部的数组所做的更改实际上是在原始数组上进行的。
#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';
}
运行它,您将看到更改
y
本身作为其自身的副本传递,但因为它是一个指针,所以您将直接对数组进行操作。请编辑您的答案。
为什么不将数组作为参数“返回”?
fillarr(int source[], size_t dimSource, int dest[], size_t dimDest)
{
if (dimSource <= dimDest)
{
for (size_t i = 0; i < dimSource; i++)
{
//some stuff...
}
}
else
{
//some stuff..
}
}
或者..以更简单的方式(但您必须知道尺寸...):
fillarr(int source[], int dest[])
{
//...
}
这是要解决的此类问题的完整示例
#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;
}
我使用了静态数组,这样在返回数组时它不应该在返回局部变量的地址时抛出错误......所以现在你可以通过将其设为静态来从函数发送任何本地创建的变量......因为它作为全局变量工作……
#include<iostream>
using namespace std;
char *func(int n)
{
// char a[26]; /*if we use this then an error will occur because you are
// returning address of a local variable*/
static char a[26];
char temp='A';
for(int i=0;i<n;i++)
{
a[i]=temp;temp++;
}
return a;
}
int main()
{
int n=26;
char *p=func(n);
for(int i=0;i<n;i++)
cout<<*(p+i)<<" ";
//or you can also print like this
for(int i=0;i<n;i++)
cout<<p[i]<<" ";
}
只需将 type[ ] 定义为返回值,例如:
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)
不定期副业成功案例分享
array
和&array
在很多情况下可以互换。int a[10]
中a
的类型是int[10]
。您可以说的是数组“衰减”为指向其第一个元素的指针。 (这是一个隐式的数组到指针的转换。)然后你的答案会和我的一样。如果您编辑答案以区分数组、数组到指针的转换和指针,我将删除我的答案,因为它们将具有相同的核心信息,而您是第一个。