如何确保从函数返回指针是安全的

时间:2012-07-02 10:36:32

标签: c++

这是我的问题: 我在这里读到StackOverflow,有时候从函数返回指向局部变量的指针是不安全的。例如:

#include<iostream>

using namespace std;

int *foo(void) {
    int x[] = {1,2,3};
    return x;
}

int main() {
    int *numbers;
    numbers = foo();
    return 0;
}

我想知道这是不安全的,考虑到x是一个本地数组,内存可能是未分配的,实现相同结果的更好方法是什么?

6 个答案:

答案 0 :(得分:10)

  

我在StackOverflow上读到,有时从函数返回指向局部变量的指针是不安全的。

返回指向局部变量的指针始终不安全。实际上这样做是错误的,并且使用此指针将导致未定义的行为。另请参阅this awesome post

如果要将数据返回到调用范围,可以使用std::vector作为副本:

std::vector<int> foo(void){
  std::vector<int> x = {1,2,3}; // using C++11 initializer list
  return x;
}

如果它是固定长度的数组(总是大小为3),则可以使用std::array代替。


根据您的要求,您也可以使用静态变量。也就是说,变量永远不会超出范围,s.t。你可以通过引用(或通过指针)安全地返回它。请注意,您只有一个副本。如果您修改它,它将保持修改。 (如果它是只读的,请设为const &。)

std::vector<int>& foo(void) {
  // this is only instantiated once when the function is first called
  static std::vector<int> x = {1,2,3}; 
  return x;
}

答案 1 :(得分:0)

首先int x = {1,2,3}是语法错误。它应该是int x[] = {1,2,3};

这是未定义的行为。因为自动数组在其块中定义了生命周期,所以它位于foo()函数内部。因此,无论何时从foo()返回,x的存储空间都不再保证为您保留。因此,如果通过指针访问该位置,则行为未定义。

为了实现相同的结果,动态分配内存。

int *foo(void){
int x[] = {1,2,3}, *x_to_return;
x_to_return = new int [sizeof (x)/sizeof(x[0])];
memcpy (x_to_return, x, sizeof (x));
return x_to_return;
}

基本上,您需要做的是使用new动态分配存储空间,将数据复制到由new分配的内存块(其基数为),并将该内存地址返回到来电者。

完成使用后不要忘记释放内存,否则你的代码会有内存泄漏。

还有一点需要注意,如果您有static int x[] = {1,2,3};之类的声明,那么您可以返回x的地址,因为在这种情况下x的生命周期是整个程序运行时。

当你的问题标记为c ++时,你应该使用vector,检查moooeeeep的答案。

答案 2 :(得分:0)

返回指向该数组的指针是不安全的(或者说是错误的),因为当函数返回时它不再存在。内存可以不仅可以解除分配,而且
请注意,它可能仍然在意外工作,但老实说,这将比它不起作用更糟糕(因为它是不可预测的,无法调试)。永远不要尝试“但似乎工作”,即使它们似乎有用。

这与返回指针或引用相同, const 引用是异常。 const引用使引用的对象在其生命周期内保持活动状态。

如果要返回指针,则动态分配或使对象static成为选项。或者,只需按值返回临时对象,依靠编译器将其转出RVO。

答案 3 :(得分:0)

你可以:

  1. 将x声明为static
  2. 将x声明为pointer

答案 4 :(得分:0)

返回指向局部变量的指针(实际上是未定义的行为)永远不会安全。对于大多数实现,它们都存在于堆栈中,因此在使用指针时可以覆盖它。

您可以返回动态分配的数组:

int* foo() { int* x = new int[3]; ..}

当然你需要手动删除指针,这使得编写健壮的,安全的代码变得困难。因此,通常最好使用矢量:

std::vector<int> foo() { 
   std::vector<int> x;
   x.push_back(1); 
   x.push_back(2); 
   x.push_back(3);
   return x;
}

如果你使用c ++ 11,你可以使用初始化列表填充向量,使代码更好:

std::vector<int> foo() { std::vector<int> x = {1,2,3};  return x; }

C ++ 11具有移动语义,这意味着在这种情况下,按值返回向量几乎不会产生任何性能。对于C ++ 03,如果性能是必不可少的,你可以给函数一个引用/指向vector作为参数并填充:

void foo(std::vector<int>& x) {x.clear(); x.push_back(1); ...}

答案 5 :(得分:-1)

是的,它是不安全的,因为数组是在堆栈上分配的,因此当函数返回时它将被释放。

不是在函数内部分配数组,而是在外部创建它并将指针传递给函数。这只是一个例子:

#include<iostream>
using namespace std;

void foo(int numbers[]){
    numbers[0] = 1;
    numbers[1] = 2;
    numbers[2] = 3;
}

int main(int args, char**argv) {
    int numbers[3];
    foo(numbers);
    cout << numbers[0] << numbers[1] << numbers[2];
    return 0;
}

这将打印“123”。