在C语言中处理内存分配的最佳方法是什么?

15

如何在C语言中处理内存是最佳实践?

没有类可以为我处理构造函数/析构函数。

  • 在函数开头分配内存还是使用一个为我创建它的函数好?如何释放它们?
  • 这些是广泛的问题,不同情况下可能有所不同,但您如何处理它们?
  • 您能给出什么提示和经验教训吗?

我认为我掌握了如何在C++中处理内存,但在C中处理内存是不同的,我有点脱节。

在C++中,我有构造函数和析构函数,我有相当简单的new和delete,并且我知道如何使用RAII、智能指针和类来封装它。

然而,在C中,我无法以同样的方式处理malloc和free。我不知道如何隐藏它们以及如何自动化事情。我唯一能想到的就是使用函数来初始化和销毁我的指针。但是我应该如何构建我的内存处理呢?

在写这篇文章时,我意识到这更多是关于我理解C流程而不是其他任何事情的问题,但是一次只问一个问题。

编辑:感谢您的回答,但我需要重新表达自己。

当我说我在C++中使用RAII和智能指针时,我不想在C中使用相同的方法,我知道它们不一样。但是我如何处理C++中的内存分配与这些技术有关。

例如,在我的类中,我动态添加和销毁类使用的内存。这样,我可以实现一种封装,我不需要知道类如何处理其内存,它只是处理它。这意味着我可以“隐藏”更低级别的内存处理,并只专注于几个“较大”的类。

12个回答

0

我不太确定你在问什么,但C语言相当简单:

struct Foo *f0 = malloc(sizeof(*f));   // alloc uninitialized Foo struct
struct Foo *f1 = calloc(1,sizeof(*f)); // alloc Foo struct cleared to all zeroes

//You usually either want to clear your structs using calloc on allocation, or memset. If 
// you need a constructor, just write a function:
Foo *Foo_Create(int a, char *b)
{
   Foo *r = calloc(1,sizeof(*r));
   r->a = a;
   r->b = strdup(b);
   return r;
}

Here is a simple C workflow with arrays:
struct Foo **foos = NULL;
int n_foos = 0;
...
for(i = 0; i < n_foos; ++i)
{
   struct Foo *f = calloc(1,sizeof(*f));
   foos = realloc(foos,sizeof(*foos)*++n_foos); // foos before and after may be different
   foos[n_foos-1] = f;
}

如果你感到有兴趣,你可以编写宏来帮助:

#define MALLOCP(P) calloc(1,sizeof(*P)) // calloc inits alloc'd mem to zero

一些要点:
  • malloc、calloc、realloc等都使用free(),因此管理这些东西很容易。只需保持一致即可。
  • malloc的性能可能会很慢。有人在上面发布了一个链接。如今,快速多线程分配是关键,可以看看tcmalloc等。你可能不必担心这个问题。
  • 在现代虚拟内存架构中,除非你的虚拟地址空间用完了,否则malloc几乎永远不会失败。如果发生这种情况,请切换到64位 ;)
  • 确保你正在使用具有边界检查、擦除自由值、泄漏跟踪和所有好东西的系统(请参见valgrind、win32调试堆等)。

-2
你可能认为我说的话很奇怪,但是C++和C之间并没有太大的区别。C也有RAII。RAII不仅属于C++。
唯一需要做到的就是有足够的纪律来进行管理。
C++类:
class foo {
public:
   ofstream ff;
   int x,y;
   foo(int _x) : x(_x),y(_x){ ff.open("log.txt"); }
   void bar() { ff<<x+y<<endl; }
};

int main()
{
   auto_ptr<foo> f(new foo);
   f->bar();
}

C对象

typedef struct FOO {
    FILE *ff;
    int x,y;
} *foo_t;

foo_t foo_init(int x)
{
   foo_t p=NULL;
   p=malloc(sizeof(struct FOO)); // RAII
   if(!p) goto error_exit;
   p->x=x; p->y=x;
   p->ff=fopen("log.txt","w");   // RAII
   if(!p->ff) goto error_exit;
   return p;
error_exit:   // ON THROW
   if(p) free(p);
   return NULL;
}

void foo_close(foo_t p)
{
   if(p) fclose(p->ff);
   free(p);
}

void foo_bar(foo_t p)
{
   fprintf(p->ff,"%d\n",p->x+p->y);
}

int main()
{
  foo_t f=foo_init(1);
  if(!f) return 1;
  foo_bar(f);
  foo_close(f);
  return 0;
}

这不是 RAII。RAII 是将资源绑定到栈上的对象,利用析构函数来处理资源释放。C 没有构造函数/析构函数语义。 此外,这段代码比 C++ 更低效,因为它测试错误,而 C++ 把它们视为异常。 - rlbond
有两个要点:a)C语言没有异常,它们是“if”条件。b)RAII不是关于析构函数,而是关于资源的正确初始化和释放。当您将代码分为三个部分时,可以在C语言中轻松完成此操作:初始化、工作和结束。 - Artyom

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接