【问题标题】:Pointer returns and scope指针返回和范围
【发布时间】:2016-11-15 03:51:09
【问题描述】:

返回一个在函数内部声明的指针是否被认为是不好的做法?示例:

int* foo(void)
{
    int * ret_val = 1234;

    return ret_val;
}

我相信你应该使用:

static int * ret_val = 1234;

但是话说回来,在你的范围之外返回内存是否仍然被认为是不好的做法?

【问题讨论】:

  • 你没有分配任何内存返回!!所以这是一个不好的做法。但是,如果您使用malloc() 来分配内存,那么由于malloced 内存可以在函数调用之间使用,那么这不是一个坏习惯。
  • *ret_val = 1234; 本身就很糟糕。至于你的问题,如果你不使用new,那么它就是stackoverflow.com/questions/6441218/…的欺骗
  • 是的,返回动态分配的对象是一种不好的做法,因为跟踪可能的内存泄漏更加困难
  • 如果这(返回分配的内存,而不是分配失败)是不好的做法,那么malloc 将是最严重的违规者。
  • 您应该选择一种语言,因为在C 中的良好做法在C++ 中可能是坏做法。两种截然不同的语言。

标签: c++ pointers memory scope


【解决方案1】:

返回一个指针不是问题,只要它指向仍在作用域内的内存,例如动态分配的内存、全局数据或在调用堆栈更上层仍然存在的局部变量。

上面代码的问题是你在没有给它分配任何东西的情况下取消引用一个指针。这:*ret_val = 1234; 将值 1234 分配给 ret_val 指向的地址,但 ret_val 没有分配任何东西。

另一方面,如果你这样做了:

int* foo(void)
{
    int * ret_val;

    ret_val = malloc(sizeof(int));
    *ret_val = 1234;

    return ret_val;
}

这很好,因为您正在返回指向动态分配内存的指针。

【讨论】:

  • “仍在作用域内的内存”:这是什么意思?
  • @ScottHunter 添加了更多详细信息。
【解决方案2】:

罪魁祸首是

*ret_val = 1234;

应用一个未初始化的指针。

我相信你应该使用:

static int* ret_val;

不,要解决这个问题,您可以使用

int* foo() {
     static int_val = 1234;
     return &int_val;
}

但如果那是你真正想要的,那是有争议的。该函数的所有调用者将共享int_val 的同一实例。

【讨论】:

  • 你为什么要这样做?不要教初学者像那样滥用static,除非您确切知道自己在做什么,否则这是一种糟糕的做法。
  • @interjay 作为Can a local variable's memory be accessed outside its scope? 的骗子,我犹豫是否要关闭它,因为问题与实际情况不完全相符。
  • 是的,这似乎不是同一个问题。但是在您的回答中,您可能应该提到您提议的代码创建了一个单例(所有调用者共享相同的指针),这不是大多数返回指针的函数所期望的。
【解决方案3】:

如果你返回一个指向局部变量的指针,这是一个非常糟糕的做法,因为你会有一个悬空指针

int* foo(void)
{
    int a = 1337;
    return &a; //Don't do this!
}

但有时它可能有用:

int* copy_array(int *array, int size)
{
    int *v = (int*) malloc (size * sizeof (int));
    //..copy the array..
    return v;
}

无论如何,你应该避免返回指针,这样就不会有内存泄漏的风险。

【讨论】:

  • 我会考虑将原始指针返回到动态分配的内存在 C++ 中是一种不好的做法
  • 是的,是的,他应该使用 STL 容器(而不是原始数组)或其他解决方案。
  • 好吧,他的回答中有标签'c',所以我使用了它。该函数在 C 中也是典型的,在 C++ 中你应该使用 STL。无论如何,我没有任何理由因此而否决这个答案。
  • 是的 OP 应该问 C 或 C++ 问题。不过,这不是我的反对意见。
  • 好吧,我不是在回复你,而是在回复刚刚删除他的答案的人,说“为什么是 malloc 而不是 new?”。
【解决方案4】:

除了您使用ret_val 而不分配它之外,从函数返回指针是公平的做法,尽管需要特别注意:

  • 不要返回指向局部变量的指针,因为它会在函数退出时被销毁,如果取消引用会导致未定义的行为
  • 如果函数分配内存,调用者函数有责任确保指针在不再需要时被释放,以避免内存泄漏
  • 如果函数以指针的形式处理数组,它需要知道数组的大小以避免未定义的行为,通常作为额外的size 参数或全局常量

【讨论】:

    【解决方案5】:

    指针就像快捷方式或超链接。仅创建快捷方式不会安装程序。创建超链接并不会神奇地设置整个网站。

    与指针相同:int* 是指向整数的指针。它不是整数本身。因此,在尝试跟随指针之前,您必须确保指针指向有意义的东西。

    这样做的一种方法是创建一个整数并设置一个指向它的指针:

    int* ret_val = new int;
    

    这将创建一个整数(new 运算符),然后设置 ret_val 指针指向它。

    通过new 创建的对象一直存在,直到您通过delete 明确销毁它们或您的应用程序结束时。因此,如果函数中有new int,即使函数结束也可以安全地访问它。

    如果您将指针 ret_val 设置为不是一个新的整数,而是设置为一个现有的局部变量,即

    int myInteger;
    int* ret_val = &myInteger;
    

    在这种情况下,ret_val 指向一个局部变量 myInteger,当包含声明的作用域结束时,该变量会被销毁。你最终会得到一个悬空指针,引用不存在的东西(想想:损坏的超链接)。使用这样的指针会导致未定义的行为。它可能会使您的程序崩溃,但它也可能会默默地接受它,修改内存中的一些随机空间。


    所以,形状的函数:

    int* foo(void)
    {
        int* ret_val = new int;
        *ret_val = 1234;
        return ret_val;
    }
    

    使用安全。这不一定是一个好习惯:此类函数的用户必须知道它创建了一个新整数,稍后 - 在某些时候 - 有人应该delete。执行此操作的函数通常以某种方式突出此类行为,例如通过它的名字。例如,allocateInt() 清楚地表明它创建了稍后应该删除的内容。相比之下,getInt() 表明整数已经存在并且没有创建任何新内容。

    【讨论】:

      【解决方案6】:

      你应该考虑:

      int *foo() {
              int *ret_val = new int(1234);
              return ret_val;
      }
      

      而不是(错误代码):

      int* foo(void)
      {
          int * ret_val;
      
          *ret_val = 1234;
      
          return ret_val; // dangling pointer!
      }
      

      否则你会有悬空指针(第二个例子)。 显然不要忘记释放内存。 您还应该考虑使用智能指针: https://en.wikipedia.org/wiki/Smart_pointer

      由于您同时指定了 C++ 和 C 语言,因此上述代码使用 C++(新运算符),请参阅(例如)@Mattia F. ANSI C 答案。

      【讨论】:

      • @Amadeus 显然不是。对于 ANSI C,应该考虑 malloc。问题中有 C++ 标签,所以应该不是问题。
      • @Amadeus 现在应该没问题
      猜你喜欢
      • 2019-12-20
      • 1970-01-01
      • 2011-07-22
      • 2013-06-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-10-07
      • 1970-01-01
      相关资源
      最近更新 更多