ChatGPT解决这个技术问题 Extra ChatGPT

在函数中返回数组

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

int fillarr(int arr[])
{
    for(...);
    return arr;
}

我怎样才能返回该数组?我将如何使用它,比如我返回了一个指针,我将如何访问它?

严格来说,在这种情况下,您不需要返回数组,因为数组是通过引用传递的,因此对“arr”内部元素的任何更改都将在函数外部看到。
返回数组便于链接函数。
只要您没有在堆栈上创建数组并返回指向它的指针的错误。
@BuggerMe:数组 not 通过引用传递(除非您使用更有趣的语法请求它),在代码中,数组 decays 成为指向第一个元素的指针,并且传递给函数。函数签名中的 5 被编译器丢弃。
@BuggerMe:不是,不是。我很准确,因为我已经习惯了人们误解 C++ 中数组的 pass-by-value 语法的语义。按引用传递数组是:void foo( int (&array)[5] );(按引用传递 5 个整数的数组)。当您通过引用传递时,您在函数中得到的是对实际类型的 reference。另一方面,void foo( int array[5] ) 在函数定义期间由编译器翻译为 void foo(int*)。调用 foo( myarray ) 会产生数组的 decay 指向第一个元素的指针。

G
GManNickG

在这种情况下,您的数组变量 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++ 语句”是错误的;数组不是指针。
记住 a[i] == *(a + i) 规则
@布伦特纳什,不。数组是数组。指向数组开头的指针是指针。碰巧编译器有一些语法糖可以在某些情况下为您进行翻译。 array&array 在很多情况下可以互换。
@Brent:不。数组是它自己的类型,它不是一种特殊的指针。 int a[10]a 的类型是 int[10]。您可以说的是数组“衰减”为指向其第一个元素的指针。 (这是一个隐式的数组到指针的转换。)然后你的答案会和我的一样。如果您编辑答案以区分数组、数组到指针的转换和指针,我将删除我的答案,因为它们将具有相同的核心信息,而您是第一个。
@seand 记住 a[i] == *(a + sizeof(a)*i) 规则
N
Nawaz

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,因此您可以应用面向对象的语义,同时保留数组的原始简单性。


+1 举例说明如何通过引用传递数组。但是你错了,你不能通过引用返回一个数组。实现它的最简单语法是使用 typedef: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:谢谢,如果您在非 typedef 语法中省略了外部括号,就会出现 Comeau 消息 error: function returning array is not allowed 的错误印象。幸运的是,今天我检查了另一个问题的左右规则并设法构造了正确的东西......在看到你说有可能......在看到你给出代码之前......
chubsdad 的答案有来自标准的正确引用:您不能返回数组,但可以返回指向数组的引用或指针。数组是不可复制的(作为一种类型),因此它们不能被返回——这意味着复制——并且当存在该语法时,编译器会将参数转换为指针。
@大卫:确实如此。此页面变得异常长。从来没有这么多人自愿编写这么多琐碎的函数,在一个地方返回一个数组。
@Potatoswatter我是cpp的新手,你能详细解释第二个代码片段吗?为了理解,我无法将它分成几部分。
c
cubuspl42

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

引用 OP:(...) you can consider the array returned arr2, totally another array (...)
C
Chubsdad

$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 的指针,而不是数组。
同样,为什么在函数内部更新实际数组时返回类型?这是最佳实践的问题吗?
S
SingleNegationElimination

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

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)
h
hookenz

这个:

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,它更安全、更容易。 (即使不是更快,也一样快。)
A
Adrian

这是一个相当古老的问题,但我将投入 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*>,您可以将它用于 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) 非常相似,但它们在这种情况下不起作用。


S
Sandeep

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

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

n
nada

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

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

  }

D
Daniel
int *fillarr(int arr[])

您仍然可以使用类似的结果

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

我不认为 'int [] fillarr ...' 是合法的。由于数组指针等价,您将使用“int *fillarr”。
S
Shehanmark

如上所述路径是正确的。但我认为如果我们只返回一个函数的局部数组变量,有时它会返回垃圾值作为它的元素。

为了避免我不得不动态创建数组并继续。这是这样的。

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

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





N
Nozama
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))

d
dcmorse

来源:https://www.tutorialspoint.com/cplusplus/cpp_return_arrays_from_functions.htm

C++ 不允许将整个数组作为参数返回给函数。但是,您可以通过指定不带索引的数组名称来返回指向数组的指针。

如果要从函数返回一维数组,则必须声明一个返回指针的函数,如下例所示:

int * myFunction() { . . . }

C++ 不提倡将局部变量的地址返回到函数外部,因此您必须将局部变量定义为静态变量。

将这些规则应用于当前问题,我们可以编写如下程序:

# 使用命名空间标准包含 ; int * fillarr( ); int main () { int *p; p = fillarr(); for (int i = 0; i < 5; i++) cout << "p[" << i << "] : "<< *(p + i) << endl;返回0; } int * fillarr( ) { 静态 int arr[5]; for (int i = 0; i < 5; ++i) arr[i] = i;返回 arr; }

输出将是:

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

A
Alexandr

那么:

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

    return f;
}

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

A
Abhishek gaur

实际上,当您在函数内部传递数组时,指向原始数组的指针会在函数参数中传递,因此对该函数内部的数组所做的更改实际上是在原始数组上进行的。

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

运行它,您将看到更改


请使用正确的英文措辞(you'll 而不是 u'll)并省略诸如“buddy”之类的空短语。
另外:“那么实际上它是作为参考传递的”是错误的。变量 y 本身作为其自身的副本传递,但因为它是一个指针,所以您将直接对数组进行操作。请编辑您的答案。
stackoverflow.com/questions/5573310/… TL;DR “因此,这两种形式是相同的。”
是的,从技术上讲,它是一个数组,你是对的,但是复制的是指向数组的指针,而不是数组本身。
G
Giovanni Paolin

为什么不将数组作为参数“返回”?

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[])
{
    //...
}

S
Shaonsani

这是要解决的此类问题的完整示例

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

S
Shubham Thakur

我使用了静态数组,这样在返回数组时它不应该在返回局部变量的地址时抛出错误......所以现在你可以通过将其设为静态来从函数发送任何本地创建的变量......因为它作为全局变量工作……

#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]<<" ";    

}

R
Ricardo Fercher

只需将 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)

这不是 C++