你应该在C程序结束时释放吗

你应该在C程序结束时释放吗,c,memory-management,C,Memory Management,假设我有一个如下的程序 #include <stdio.h> #include <stdlib.h> int main(int argc, char *argv[]) { if (argc < 2) return 1; long buflen = atol(argv[1]); char *buf = malloc(buflen); fread(buf, 1, buflen, stdin); // Do stuff with buf fr

假设我有一个如下的程序

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[]) {
  if (argc < 2) return 1;
  long buflen = atol(argv[1]);
  char *buf = malloc(buflen);
  fread(buf, 1, buflen, stdin);

  // Do stuff with buf

  free(buf);
  return 0;
}
#包括
#包括
int main(int argc,char*argv[]){
如果(argc<2)返回1;
long buflen=atol(argv[1]);
char*buf=malloc(buflen);
fread(buf,1,buflen,stdin);
//用buf做东西
免费(buf);
返回0;
}
这类程序通常具有更复杂的清理代码,通常包括对
free
的多次调用,有时还包括用于错误处理的标签甚至清理函数

我的问题是:最后的
免费(buf)
真的有必要吗?我的理解是,当程序退出时,内核将自动清理未绑定的内存,但如果是这样,为什么在代码末尾释放这样一种常见模式


BusyBox提供了一个编译选项,用于在执行结束时禁用自由调用。如果这不是问题,那么为什么有人会禁用该选项?这纯粹是因为像Valgrind这样的程序在分配的内存未释放时检测到内存泄漏吗?

实际上,就像在绝对情况下一样?在现代操作系统上,不是。在某些环境中,是的

清理分配的所有内容总是一个很好的计划,因为这样可以很容易地扫描内存泄漏。如果您在退出之前有未完成的分配,您就有漏洞。如果你因为操作系统为你做而不
免费
东西,那么你就不知道这是一个错误还是故意的行为

您还应该检查任何可能返回错误的函数的错误,如
fread
,但您没有,因此您已经牢牢地处于危险区域。这是一个关键任务代码,如果它崩溃了,坏事情会发生吗?如果是这样的话,你会想做每一件事都绝对按书办事

正如Jean François所指出的,这种琐碎代码的组成方式是一个糟糕的例子。大多数程序看起来更像这样:

void do_stuff_with_buf(char* arg) {
  long buflen = atol(arg);
  char *buf = malloc(buflen);
  fread(buf, 1, buflen, stdin);

  // Do stuff with buf

  free(buf);
}

int main(int argc, char *argv[]) {
  if (argc < 2)
    return 1;

  do_stuff_with_buf(argv[1])

  return 0;
}
void do_stuff_与_buf(char*arg){
long buflen=原子(arg);
char*buf=malloc(buflen);
fread(buf,1,buflen,stdin);
//用buf做东西
免费(buf);
}
int main(int argc,char*argv[]){
如果(argc<2)
返回1;
用(argv[1])填充
返回0;
}

这里应该更明显的是,
do_stuff\u with_buf
函数应该自己清理,它不能依赖程序退出来释放资源。如果该函数被多次调用,则不应泄漏内存,这只是草率的,可能会导致严重的问题。失控的分配会导致臭名昭著的Linux“OOM killer”之类的东西出现,并进行疯狂的谋杀以释放一些内存,这通常只会导致混乱和混乱。

事实上,就像绝对?在现代操作系统上,不是。在某些环境中,是的

清理分配的所有内容总是一个很好的计划,因为这样可以很容易地扫描内存泄漏。如果您在退出之前有未完成的分配,您就有漏洞。如果你因为操作系统为你做而不
免费
东西,那么你就不知道这是一个错误还是故意的行为

您还应该检查任何可能返回错误的函数的错误,如
fread
,但您没有,因此您已经牢牢地处于危险区域。这是一个关键任务代码,如果它崩溃了,坏事情会发生吗?如果是这样的话,你会想做每一件事都绝对按书办事

正如Jean François所指出的,这种琐碎代码的组成方式是一个糟糕的例子。大多数程序看起来更像这样:

void do_stuff_with_buf(char* arg) {
  long buflen = atol(arg);
  char *buf = malloc(buflen);
  fread(buf, 1, buflen, stdin);

  // Do stuff with buf

  free(buf);
}

int main(int argc, char *argv[]) {
  if (argc < 2)
    return 1;

  do_stuff_with_buf(argv[1])

  return 0;
}
void do_stuff_与_buf(char*arg){
long buflen=原子(arg);
char*buf=malloc(buflen);
fread(buf,1,buflen,stdin);
//用buf做东西
免费(buf);
}
int main(int argc,char*argv[]){
如果(argc<2)
返回1;
用(argv[1])填充
返回0;
}

这里应该更明显的是,
do_stuff\u with_buf
函数应该自己清理,它不能依赖程序退出来释放资源。如果该函数被多次调用,则不应泄漏内存,这只是草率的,可能会导致严重的问题。失控的分配可能会导致臭名昭著的Linux“OOM杀手”之类的东西出现,并进行疯狂的谋杀以释放一些内存,这通常只会导致混乱和混乱。

有必要吗?在大多数情况下不会。但事后清理是一个好习惯。为了补充前面的评论,假设您使用不同的小程序(如示例)来构建另一个更大的程序/应用程序。或者以某种方式将其转换为一个守护进程,使其保持运行并多次执行您的函数。如果您没有将任何
空闲的
,则在将这些片段组合在一起/使其在循环中运行时很容易忘记添加它。现在你的程序有严重的内存泄漏。有必要吗?在大多数情况下不会。但事后清理是一个好习惯。为了补充前面的评论,假设您使用不同的小程序(如示例)来构建另一个更大的程序/应用程序。或者以某种方式将其转换为一个守护进程,使其保持运行并多次执行您的函数。如果您没有将任何
空闲的
,则在将这些片段组合在一起/使其在循环中运行时很容易忘记添加它。现在,您的程序出现了严重的内存泄漏。检查
fread
中的错误很有意义。这只是一个例子,而不是实际的代码。它没有资源跟踪。如果你不释放内存,直到重新启动(使用ctrl+A+A:)你才能得到它。太棒了,谢谢你提供的额外细节!另一个原因是:如果有一天你要把main变成一个服务/无止境的循环,你会很高兴你正确地处理了空闲。@Jean-Françoisfafre或使用valgrind
--leak kinds=all
,如果你的操作系统支持的话。检查
fread
的错误是一个很好的观点。不过,这只是一个例子,而不是实际情况