【问题标题】:C: Scope of a struct that contains dynamically allocated member?C:包含动态分配成员的结构的范围?
【发布时间】:2016-01-18 11:28:59
【问题描述】:

我有一个结构和一个返回 Foo 实例的函数,定义如下:

struct Foo
{
    int a;
    int* b;     
};

struct Foo makeFoo(int a, int bSize)
{
    struct Foo foo;
    foo.a = a;
    foo.b = malloc(sizeof(int) * bSize);
    for (int i = 0; i < bSize; ++i)
        foo.b[i] = i;

    return foo;
}

最初,我认为foo 是一个局部变量,当makeFoo 返回时它会消失,但从这个问题Is it safe to return a struct in C or C++?,我知道这样做是安全的。

现在我的问题是什么时候会收集foo 的内存?我必须先freeb 成员吗?

假设我像这样使用makeFoo

void barFunc()
{
    struct Foo foo = makeFoo(3, 10);

    printf("Foo.a = %d;\nFoo.b = [", foo.a);
    for (int i = 0; i < 10; ++i)
        printf("%d, ", foo.b[i]);

    printf("\n");
}

void main(int argc, char* argv)
{
    barFunc();  
}

barFunc 返回并且我回到main 时,foo 的内存是否已收集?或者我必须在barFunc 末尾拨打free(foo.b) 吗?

【问题讨论】:

  • 如果你有一个malloc,你需要一个免费的来释放它。如果你没有空闲,那么它仍然被分配。
  • 是只保留foo.b 的内存还是整个结构?
  • @user3629249 - 糟糕的编程习惯是否值得商榷。但是,返回结构的行为是明确定义的。
  • @wildplasser 有点错过目标,我尽可能避免使用 java ;)
  • @user3629249 请理解编码中(几乎)没有绝对的“正确”和“错误”。有指导方针、最佳实践等,它们的存在是有充分理由的。而且,您总是会发现最好遵循它们的边缘情况。以“永远释放你 malloc 的东西”为例。如果您有一个简单的“管道和过滤器”架构,并且您的一个过滤器需要为一个块分配大量内存,但在处理后退出,那么大量 free() 调用只会浪费代码大小和执行时间。

标签: c struct scope dynamic-memory-allocation


【解决方案1】:

最初,我认为 foo 是一个局部变量,当 makeFoo 返回时它会消失,但是从这个问题 Is it it safe to return a struct in C or C++?,我知道这样做是安全的。

确实意识到本地的foo 确实消失了?像你在这里所做的那样按值返回 struct 只是将其内容复制到调用者提供的实例中。(*)

但是,当然,内容包括一个指向你用malloc()分配的内存的指针。所以以后一定要free()d。

(*) 对于小型结构,这 可能 是个好主意,具体取决于您的需要,但请始终记住,整个内容都是复制的——对于某种大型结构,这绝对不是您想要的结构。

【讨论】:

    【解决方案2】:

    barFunc 中的foo 变量超出范围之前,您必须自己调用free(foo.b)。这在makeFoo 函数中是不必要的,因为foo 和分配内存的指针被复制到调用者,所以没关系。

    既然你有一个makeFoo 函数,那么最好也有一个deleteFoo 函数:

    void deleteFoo(struct Foo *foo)
    {
      free(foo->b);
    }
    
    void barFunc()
    {
      struct Foo foo = makeFoo(3, 10);
      ...
      deleteFoo(&foo);
    }
    

    【讨论】:

    • 没有。请参阅@Felix Palman 的回答 - 返回结构复制该结构的内容,因此指针的值被传递给调用者,调用者可以处理调用free()
    • 我想我也是这么说的。
    • "在 barFunc 中的 foo 变量超出范围之前,您必须自己调用 free(foo.b)。" - 不,指向malloc()'d 内存的指针被返回给调用者,因为结构被返回。指针的值被复制到调用者的struct Foo,并且可以被调用者free()'d。这与只传递一个指针完全相同——指针的副本也超出了分配函数的范围,但它的值被返回了。
    • 加上我的 2¢:“超出范围”在这里可能只是草率的措辞。一旦创建结构的函数结束,它就会“超出范围”,但副本已传递给调用者。你的意思是(你的措辞“在barFunc”中有点清楚)在它丢失之前。
    • 试图让它更清楚:它不会介意在barFunc 中“超出范围”,只要它会被传递到其他地方。忽略这个非常小的措辞不准确,我会说这是一个正确的答案。
    【解决方案3】:

    是的,从 C/C++ 函数返回结构体是否安全,但是像 struct fooo{int a[1024*1024*1024];}; 这样具有大量静态数据的结构体可能会导致程序崩溃。因此,为类似的结构返回指向已分配内存块的指针要安全得多。

    【讨论】:

    • 这不是“正确的方法”。按值返回结构是完全合法且定义明确的 - 与仅按值返回指针一样是完全合法且定义明确的。
    • 好吧,不是我的反对意见,而是我认为,返回一个结构是完全安全的(但如果结构对于做这样的事情来说不是相当小,则可能效率低下)
    • 我应该删除我的帖子,还是保留它
    • @milevyo 很简单:return 接受一个传递给调用者的值。在结构的情况下,这可能是相当多的工作(将所有内容复制到调用者声明的变量中)。 如果这个值恰好是一个指针并且它指向本地堆栈上的某个值,那么(只有这样)你会遇到问题,因为这个堆栈已经消失了当函数退出时。
    • 我现在明白了,函数在终止前将堆栈中的局部变量的内容复制到LValue。
    猜你喜欢
    • 2021-08-25
    • 2020-05-26
    • 2020-03-06
    • 2015-01-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-06-11
    相关资源
    最近更新 更多