【问题标题】:When use malloc for a static pointer inside a function?何时将 malloc 用于函数内的静态指针?
【发布时间】:2015-12-22 16:18:32
【问题描述】:

我不明白为什么,在下面的代码 sn-p 中,程序员使用了一个静态变量(因此我开始怀疑我所有的小知识):

int func (...) {
    static unsigned int *temp = NULL;

    if( temp == NULL ) {
        temp = malloc(dim*sizeof(*temp));
    }

    // do stuff with temp

}

在main()中,func在一个循环中被多次调用:

int main() {

    for (i = 0; i < N; ++i)
        x = func(...);

}

第一次调用 func 将变量 temp 初始化为 NULL,因此 temp 被分配(也许初始化也是多余的(根据 post)。

func 函数对 temp 没有任何特殊作用,如果满足条件,只需将一些值从另一个(全局)数组复制到它并返回写入的元素数。

如果我理解正确,malloc 会在堆中分配内存,因此内存将是持久的,直到它被显式释放,并且由于指针是静态的,它可以在后续调用中访问。

然后是问题:

  1. 我理解正确吗?
  2. 这种方法而不是 malloc temp 的优点是什么 在 func 之外,然后将其显式传递给 func?我猜 视情况而定,好的,但也许有一个众所周知的 例子。我也看到了这个answer,但我无法理解 第一个例子的用处。
  3. 我必须在 func 中释放 temp(在最后一步,顺便说一下,这段代码中没有这样做)?

我需要修改上面的代码(这要复杂得多),我更喜欢在 func 之外分配 temp 然后将它传递给函数,我想确保我没有遗漏一些重要的东西。顺便说一句,我认为我可以学到一些东西:)。

谢谢。

【问题讨论】:

    标签: c pointers static


    【解决方案1】:

    “第一次调用func,将变量temp初始化为NULL”。

    不,您不(完全)正确。静态变量由环境在任何函数(包括)main 被调用之前初始化。这就是静态变量的初始化器必须是 constant-expressions 的原因。正如您正确理解的那样,如果没有显式初始化程序,它也会在启动期间设置为 null 指针。这里是否显式更好是值得怀疑的。显式初始化器的问题在于它们在程序存储中消耗了额外的空间。对于裸机嵌入式系统,这会花费宝贵的闪存/ROM 存储空间。一些编译器(例如 gcc)提供了规避这种惩罚的选项。

    因此,func 的第一次调用将有效地分配对象,然后可以在后续调用中使用该对象。请注意,函数(及其调用者)必须正确处理分配失败。

    不使用全局变量的原因是信息隐藏。一般来说,您应该在尽可能小的范围内(并且是合理的)定义变量。这样您就可以确保变量不会在其他地方被修改。并且名称冲突的可能性降低到最低限度。由于 C 没有用户定义的命名空间,所有这些名称都会进入一个全局池,这使得这个问题非常相关。

    freeing 这种分配的数据可以通过多种方式处理。一种是添加参数以显式请求freeing。或者,可以有一个此类块的全局注册表(基本上是一个简单的链表)。在程序终止时,框架函数将遍历此列表并释放所有块。

    或者,程序可以只依赖运行时环境,它会在退出时自动释放分配的存储空间。虽然合法,但这不是 IMO 最干净的方式。

    【讨论】:

    • 一个非常明确的答案,谢谢。您提到了全局注册表,但我不明白它是如何工作的,您如何填充它?
    • @terencehill: 1) 请记住接受其中一个答案,如果这对您有帮助的话。 2)您需要一个具有自己功能的额外模块。 func 必须在此注册表中注册一次分配的块,例如blocks_register(temp);.
    • 1) 我记得,我发现大部分答案都很有用并点赞,我等着想。 2) 再次感谢。
    【解决方案2】:
    1. 我理解正确吗?

    对于所有实际意图和目的,您正确理解了 temp 的初始化,包括显式初始化程序是多余的。您似乎也正确理解了分配,以及 temp 被声明为 static 的含义。在实践中,temp 可能被初始化为 NULL before func() 被调用过,即使它从未被调用过,但程序本身无法区分它在第一次调用时被初始化。

    1. 这种方法比 func 外部的 malloc temp 然后显式传递给 func 的优点是什么?我想这取决于情况,好的,但也许有一个众所周知的例子。我也看到了这个答案,但我无法理解第一个示例的用处。

    大概需要的存储量——由变量dim 控制——直到运行时才知道。这就是动态分配的原因。

    将变量放在函数的局部范围内可确保它没有链接——它是不可见的,并且不能从函数外部的任何地方(直接)引用。另一方面,它由函数本身管理。其他函数不需要为这个函数准备一个数组或者传递一个数组给它,这个函数也不需要依赖任何其他人来正确设置数组。

    作为一种风格问题,我倾向于考虑使用静态文件范围指针(在这种情况下,令人困惑的是,static 关键字表示不同的东西:内部链接而不是静态持续时间)。唯一的区别是变量的可见性。

    然而,最终可见性的选择和变量的供应是设计问题,在某种程度上有多种解决方案。您没有提供足够的信息让我们推断您正在检查的代码中做出的具体选择。

    1. 我必须在 func 中释放 temp(在最后一步,顺便说一下,这段代码中没有这样做)?

    如果temp 被释放,那么它应该在func() 内完成。可以将temp 指针的副本暴露给函数外部的代码,在这种情况下,可以通过该指针释放分配的空间,但是temp 将作为无效指针留下,没有办法func()告诉。

    但是,创建tempstatic 的目的肯定是为了在多个函数调用中保留该值,这样分配的空间就不需要释放也不会泄漏。特别注意,因为temp 具有静态持续时间,所以对NULL 的初始化就像在程序初始化时一样执行,不是每次调用函数时。

    实际上,在这种情况下,可能没有必要释放temp。不这样做不会造成内存泄漏,因为分配给temp 的内存将继续无限期可用(尽管仅在func() 的范围内)。当程序退出时,该内存将返回给操作系统,就像当时任何其他未完成的内存分配一样。

    【讨论】:

      【解决方案3】:
      1. 我理解正确吗?

      是的,你知道。

      1. 这种方法比 func 外部的 malloc temp 然后显式传递给 func 的优点是什么?我想这取决于情况,好的,但也许有一个众所周知的例子。我也看到了这个答案,但我无法理解第一个示例的用处。

      优点是函数的调用者不需要知道func需要一个缓冲区来实现功能。

      1. 必须在 func 中释放 temp(在最后一步,顺便说一下,这段代码中没有这样做)?

      是的,你应该这样做。不幸的是,它的实现方式,你不能这样做。在func 之外无法访问temp。 func 没有 free 它是因为它的实现方式。

      为了freefunc中分配的内存,你可以使用类似的东西:

      static unsigned int *temp = NULL;
      
      void free_temp()
      {
         free(temp);
      }
      
      int func () {
          if( temp == NULL ) {
              temp = malloc(dim*sizeof(*temp));
              // Register free_temp to be called at exit time.
              atexit(free_temp);
          }
      
          // do stuff with temp
      }
      

      【讨论】:

      • 谢谢,我没有想到 atexit()。
      【解决方案4】:

      我理解正确吗?

      是的。

      这种方法比 func 之外的 malloc temp 然后显式传递给 func 有什么好处?

      在一种情况下,每个调用者都必须知道temp 及其对func 的正确初始化,从而使调用者强耦合到func。如果在将来的任何时候需要更改 temp 数组的类型或大小,每个调用者都会受到影响。在另一种情况下,所有这些都抽象出来。

      我必须在 func 中释放 temp?

      temp 在整个程序生命周期中使用。如果你在函数结束时释放它,你就违背了它的目的,即减少对malloc 的调用次数和/或在调用之间保留数据。如果您找到方法,您可以在程序结束时释放它。如果你在函数结束时释放它,那么你不需要它是静态的,你不需要if (temp == NULL) 测试。 IOW 你失去了问这个问题的理由。

      【讨论】:

        【解决方案5】:
        1. 是的。
        2. 由于我们使用的是 C 而不是 C++,我想这是一种尝试限制分配内存的所有权和管理的方法,这样被调用者就不必关心了。将该指针传播到管理它的一个函数之外是否有好处?
        3. 是的。除非您打算缓存该数据或将其保留到程序终止。

        【讨论】:

          猜你喜欢
          • 2010-12-19
          • 1970-01-01
          • 2021-01-24
          • 1970-01-01
          • 1970-01-01
          • 2014-06-26
          • 1970-01-01
          • 2012-05-05
          • 2020-08-04
          相关资源
          最近更新 更多