【问题标题】:To write robust C program, how do you avoid too many different free () combinations?要编写健壮的 C 程序,如何避免太多不同的 free() 组合?
【发布时间】:2012-03-28 06:01:06
【问题描述】:

例如, 我需要 malloc 两块内存,所以:

void *a = malloc (1);

if (!a)
  return -1;

void *b = malloc (1);

if (!b)
{
  free (a);
  return -1;
}

请注意,如果第二个 malloc 失败,我必须先释放“a”。问题是,如果有很多这样的 malloc 和错误检查,这可能会非常混乱,除非我使用臭名昭著的“goto”子句并仔细安排 free 的顺序和标签:

void *a = malloc (1);

if (!a)
  goto X;

void *b = malloc (1);

if (!b)
  goto Y;

return 0; //normal exit

Y:
  free (a);
X:
  return -1;

对于这种情况,您有什么更好的解决方案吗?提前致谢。

【问题讨论】:

  • +1 一旦我也遇到了同样的问题,但完成了“乱码”的工作..!!

标签: c memory-management


【解决方案1】:

我们这样做:

void *a = NULL;
void *b = NULL;
void *c = NULL;
a = malloc(1);
if (!a) goto errorExit;
b = malloc(1);
if (!b) goto errorExit;
c = malloc(1);
if (!b) goto errorExit;

return 0;
errorExit:
//free a null pointer is safe.
free(a);
free(b);
free(c);
return -1;

【讨论】:

  • +1 指出 free 接受空指针。我不知道是这样的。
  • 即使没有(或者如果您需要释放一些其他资源,而相应的清理功能没有这样做),您也可以随时使用if (a) free(a); 等等。
【解决方案2】:

在我看来,使用 goto 并不是一件坏事。用它来做资源清理就很合适了。

与 Linux 内核一样著名的源代码使用该技术。

只是不要使用 goto 向后退。这会导致灾难和混乱。我的建议只是向前跳。

【讨论】:

  • 不仅 Linux 内核这样做,FreeBSD 也这样做。完全同意这是正常做法。
  • 很久以前我还是用纯 C 做同样的事情。现在(使用 C++)我使用智能指针。
  • 还有; free(NULL) 不是无操作吗?意思就是把所有东西都放出来就没有问题了?
【解决方案3】:

正如前面提到的 Zan Lynx 使用 goto 语句。

您还可以分配更大的内存块以供进一步使用。

或者您可以花时间开发类似memory pool 的东西。

【讨论】:

    【解决方案4】:

    或者这样做。

     void *a,*b;
     char * p = malloc(2);
     if (!p) return -1;
     a = p;
     b = p+1;
    

    【讨论】:

      【解决方案5】:

      我认为 OOP 技术可以为您提供一个很好且干净的解决方案:

      typedef struct {
          void *a;
          void *b;
      } MyObj;
      
      void delete_MyObj(MyObj* obj)
      {
          if (obj) {
              if (obj->a)
                  free(obj->a);
              if (obj->b)
                  free(obj->b);
              free(obj);
          }
      }
      
      MyObj* new_MyObj()
      {
          MyObj* obj = (MyObj*)malloc(sizeof(MyObj));
          if (!obj) return NULL;
          memset(obj, 0, sizeof(MyObj));
      
          obj->a = malloc(1);
          obj->b = malloc(1);
      
          if (!obj->a || !obj->b) {
              delete_MyObj(obj);
              return 0;
          }
      
          return obj;
      }
      
      int main()
      {
          MyObj* obj = new_MyObj();
          if (obj) {
              /* use obj */
              delete_MyObj(obj);
          }
      }
      

      【讨论】:

      • 如果您需要多个对象怎么办?然后你又回到了你开始的地方。
      【解决方案6】:

      您的 goto 代码 IMO 并没有什么问题(我会使用更详细的标签)。

      不过,在这种情况下,您编写的 goto 语句创建的结构与反转 ifs 完全相同。

      也就是说,不离开任何范围的条件前向 goto 与没有 elseif 语句完全相同。不同之处在于goto 发生 不离开范围,而if 约束 不离开范围。这就是为什么if 通常更容易阅读:读者前面有更多线索。

      void *a = malloc (1);
      if (a) {
          void *b = malloc (1);
          if (b) {
              return 0; //normal exit
          }
          free(a);
      }
      return -1;
      

      对于几个级别,这是可以的,尽管走得太远,您会得到带有太多缩进级别的“箭头代码”。由于完全不同的原因,这变得不可读。

      【讨论】:

        【解决方案7】:

        使用像 boehmgc 这样的垃圾收集器。

        它有效,易于使用,没有与普遍看法相反的减速。

        https://en.wikipedia.org/wiki/Boehm_garbage_collector

        http://www.hpl.hp.com/personal/Hans_Boehm/gc/

        【讨论】:

          【解决方案8】:

          根据您的应用程序正在运行的平台/上下文,在 malloc() 返回 NULL 之后继续可能不再有意义。

          所以在很多情况下,一个简单的

          if (!a)
            catastrophic_failure_bail_out();
          

          可能是最明智的解决方案并且保持代码干净可读。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2010-11-30
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多