【问题标题】:Keeping the contents of an array after its function call ends. (C++)在函数调用结束后保留​​数组的内容。 (C++)
【发布时间】:2009-08-03 01:41:56
【问题描述】:

假设我有以下代码。

double *return_array(void) {
   double foo[2];
   foo[0] = 5;
   foo[1] = 6;
   cout << foo << endl;
   cout << foo[0] << endl << foo[1] << endl;
   return foo;
}

double *bar = return_array()
cout << bar << endl;
cout << bar[0] << endl << bar[1] << endl;

现在,bar 和 foo 仍然是同一个指针,但那里的内容已经完全改变了。我怎样才能解决这个问题?基本上,我需要从一个函数中传递 6 个或 9 个双打。我该怎么办?

【问题讨论】:

  • 除了下面的好回复之外,如果您有固定数量的值,您可能希望用它们创建一个 struct|class 并返回它。根据您的值的使用,它可能更容易阅读/处理,例如使用 g=foo[gravity] 代替: g=foo.gravity

标签: c++ arrays memory


【解决方案1】:

使用向量。

std::vector<double> return_array(void) {
   std::vector<double> foo;
   foo.push_back(5);
   foo.push_back(6);
   cout << foo[0] << endl << foo[1] << endl;
   return foo;
}

这是一个更好的方法,所以你避免复制向量:

void fillVector(std::vector<double>& vec)
{
    vec.push_back(5);
    vec.push_back(6);
}

int main()
{
    std::vector<double> vec;

    fillVector(vec);
}

现在,bar 和 foo 还是一样的 指针,但那里发生了变化 完全。

因为foo是在函数的栈上分配的,所以当函数返回时它会被释放。所以,bar 实际上没有指向任何地方!

【讨论】:

    【解决方案2】:

    通常您会将预先分配的内存传递给函数:

    int barsize = 2;
    double *bar = new double[barsize];
    fill_array( bar, barsize );
    cout << bar << endl;
    cout << bar[0] << endl << bar[1] << endl;
    delete [] bar;
    
    void fill_array( double *foo, int foosize )
    {
      if ( foosize < 2 )
        return;
    
      foo[0] = 5;
      foo[1] = 6;
      cout << foo << endl;
      cout << foo[0] << endl << foo[1] << endl;
    }
    

    我使用的规则是……总是在同一个地方分配和删除内存。

    或者使用 std::vector。他们很好 =) 我不再使用数组了。

    【讨论】:

    • 但是,只有在您知道在调用函数之前需要分配多少时,这才有效。如果结果的大小可以变化,则传递预先分配的空间将不起作用。
    • @Dav:这就是为什么很多 C 函数都有预飞行模式的原因。传递一个 NULL 指针,它会返回您需要的大小。分配内存并再次调用。这就是我们喜欢 C++ std::vector 的原因。在同一个地方分配和删除的规则是好的。
    • 是...如果可以的话,改用向量。
    • 是的。这就是我要找的。刚刚从函数中取出了 double* bar 声明。简单易懂。谢谢。
    【解决方案3】:

    使用new关键字在堆上而不是栈上分配内存:

    double *return_array(void) {
        double * foo = new double [2];
        foo[0] = 5;
        foo[1] = 6;
        return foo;
    }
    

    然后调用该函数的代码最终必须在使用delete 完成后释放内存:

    double * foo = return_array();
    // ...
    delete [] foo;
    

    【讨论】:

    • 您应该始终在创建内存的同一位置删除。这将导致由于误用而导致内存泄漏。
    • 同意,尽管您仍然可以通过创建一个补充函数来释放内存来遵循上述函数的规则。但我通常会按照您在回答中的方式进行操作。
    • ... 或者您可以返回一个 auto_ptr 而不必担心 =)
    • 你的答案等同于@Kieveli 的答案吗? new 究竟是做什么的?
    • 它的工作原理是一样的。我想我们是同时发的,或者我是在杰里米之后发的。
    【解决方案4】:

    使用new 分配不在函数范围内的内存。

    完成后不要忘记delete[]内存。

    【讨论】:

      【解决方案5】:

      您可以在堆上分配结果,或者更好的是,使用 std::vector。

      【讨论】:

        【解决方案6】:

        如果您总是返回固定数量的元素并愿意使用 TR1 扩展(或 Boost),我会选择 std::tr1::tuple 而不是 vector 或数组。它是一个模板类,因此您需要按照以下方式对它进行 typedef:

        typedef std::tr1::tuple<double, double, double, double, double, double> six_tuple;
        

        使用 make_tuple() 和适当数量(和类型)的参数来创建函数返回的元组,并使用模板化的 get&lt;N&gt;() 函数访问其内容,例如:

        six_tuple foo = std::tr1::make_tuple(1.0, 2.0, 3.0, 4.0, 5.0, 6.0);
        std::cout << std::tr1::get<0>(foo) << std::endl; // prints '1.0'
        

        编译器将自省make_tuple() 的参数数量和类型,如果您尝试分配错误类型的元组,则会引发错误。

        我更喜欢这个,而不是自己管理内存,但你的里程可能会有所不同。

        【讨论】:

        • 清如泥。下一个试图弄清楚你在做什么的开发人员会将你的名字添加到诅咒词列表中。
        • 什么不清楚?我承认模板参数看起来很奇怪,但这就是 typedef 的用途。 tuple 是 TR1 标准的一部分,并且支持数组不支持的各种 C++ 特性。您可以将它们与比较运算符(等于、小于、&c)一起使用,作为流运算符的参数,等等。另外,如果 return_array() 中的某些内容引发异常,则元组(如果已创建)会在堆栈展开时自毁。您无法通过 new 进行分配。如果 std::tr1::tuple 吓到你,我只能说,std::tr1::bind 会让你尖叫。
        • 我可以想象任何学习 C++ 基础知识的人都会问我的问题。您的回答我会被经验丰富的程序员很好地接受,但对于这种情况来说太过分了。不过,感谢您提供的详细信息。
        • 这不会吓到我个人 - 我有 11 年以上的 C++ 经验。它会吓到其他经验较少的开发人员。我仍然需要查找元组,因为它不经常使用,这将花费我更多的时间。那我想知道你为什么不使用向量。然后我会寻找你的评论,解释为什么你以一种 80-90% 的其他开发人员甚至没有考虑过的方式编写了一些东西。将您的非正统编码视为我的“对代码的恐惧”是一个可疑的逻辑论点,遗憾的是我不是第一次听到它。
        猜你喜欢
        • 1970-01-01
        • 2014-04-04
        • 1970-01-01
        • 2015-12-29
        • 2022-11-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-03-11
        相关资源
        最近更新 更多