【问题标题】:Can I constantly malloc memory without free?我可以在没有空闲的情况下不断地 malloc 内存吗?
【发布时间】:2020-08-12 00:21:57
【问题描述】:

这是我的代码:

struct movie {
    char movie_name[30];
    float score;
    struct movie *next;
};

typedef struct movie *p_movie;
void print_all(struct movie *head);

int main() {
    p_movie head = NULL;
    p_movie new_node = malloc(sizeof(struct movie));
    if (new_node == NULL) {
        exit(1);
    }
    strcpy(new_node->movie_name, "Avatar");
    new_node->score = 9.5f;
    new_node->next = NULL;
    if (head == NULL)
        head = new_node;
    print_all(head);
    new_node = malloc(sizeof(struct movie));
    if (new_node == NULL) {
        exit(1);
    }
    strcpy(new_node->movie_name, "Aladdin");
    new_node->score = 8.0f;
    new_node->next = NULL;
    p_movie temp = head;
    head = new_node;
    new_node->next = temp;

    print_all(head);
}

void print_all(struct movie *head) {
    printf("---------------------\n");
    printf("Head address = %zd\n", (size_t)head);
    struct movie* search = head;
    while (search != NULL) {
        printf("%zd \"%s\" %f %zd\n", (size_t)search, search->movie_name,
               search->score, (size_t)search->next);
        search = search->next;
    }
}

我因为malloc而感到困惑:它运行良好,但我不知道为什么这段代码运行良好。

我的问题是关于new_node:首先,我将内存分配给new_node,我不释放它并再次分配内存。

那么,第一个内存 (Avatar) 发生了什么?是不是删了还是保存在某个地方..?

【问题讨论】:

  • 虽然有你可以分配的空闲(虚拟)内存。 C 中没有垃圾收集器,当您丢失了分配块的地址时,该地址对您“丢失”但仍存在于内存中,这就是内存泄漏。
  • 我建议你不要像在typedef struct movie* p_movie; 中那样使用typedef 来定义'pointer to' 类型,隐藏指针会使代码难以阅读,并且是一种容易引入错误的好方法
  • 您不需要p_movie temp = head;,您只是在混淆自己。正在发生的是new_node->next = head; head = new_node;,这是一种称为前向链接的方法,它在列表的前面添加节点(如堆栈)。不利的一面是,您的列表与输入的顺序相反。

标签: c memory memory-management linked-list malloc


【解决方案1】:

我可以在没有空闲的情况下不断地 malloc 内存吗?

是的,在实践中,许多程序在初始化时 malloc 会获取一些数据,而无需打扰 free 它。但是如果你经常这样做,你就会有一个巨大的memory leak

不,理论上。阅读 C11 标准n1570

不要忘记malloc 可能会失败。

在启动时编写一个malloc 数据的程序确实有意义;对其进行复杂的计算,只需exit-s。大多数(但不是全部)operating systems 可以处理这个问题,因为当process 终止时,操作系统kernel(例如Linux)将释放进程消耗的资源。阅读virtual address space 和一本关于operating systems 的好教科书。

但是这样的做法是丑陋而脆弱的。如果以这种方式编码,您将无法将程序转换为可重用的库。几个严肃的程序(包括GCC 编译器,在某个时间点)在exit-ing 或returning 从他们的main 之前执行malloc 而没有free-ing 或returning

在 Linux 上,另请参阅 pmap(1)mmap(2) - 由 malloc(3)- 和 strace(1) 使用,当然还有 valgrind

注意世界上最快的malloc。实际上不是很有用。 ?(但符合标准)。

还要注意memory overcommitement

【讨论】:

    【解决方案2】:

    首先,我将内存分配给new_node。而且我没有释放它并再次分配内存。那么,第一个记忆(阿凡达)是怎么回事? 是不是删了还是保存在某个地方..?

    只要你没有free()d 之前调用内存管理函数分配的内存,它就会驻留在内存中直到程序完成。

    我可以在没有free 的情况下一直使用malloc 内存吗?

    这取决于您的内存容量(它能够容纳不断汇总的未释放内存块并分配新内存的时间)。在某个时刻,如果您继续储存内存,您将内存不足,您的程序和工作机器可能会崩溃。


    尽管如此,存储未使用的内存并不是一个好习惯,即使您的内存在技术上可以处理它。始终free() 不再需要动态内存。

    【讨论】:

      【解决方案3】:

      我可以在没有空闲的情况下不断地 malloc 内存吗?

      理论上,是的。在实践中,只有在内存耗尽之前。

      首先,我将内存分配给new_node

      我没有释放它并再次分配内存。

      那么,第一记忆(阿凡达)发生了什么?

      删除了吗?还是保存在某个地方..?

      在您的具体示例中:如果head == NULL,则(显然)已保存。这里,“it”指的是指向该内存的指针。

      如果 head 不是 NULL(这在那个地方是不可能的),那么您就会发生内存泄漏:您分配了无法再引用的内存。

      一般来说,在malloc()之后,你可以把这段内存留到需要的时候。这包括保留它直到您的程序结束。但是如果你不保存指向那个内存的指针,你就不能对它做任何事情:既不使用也不释放它。在这种情况下,您的程序需要比应有的更多内存,如果这种情况经常发生,它最终会不必要地阻塞您的内存。

      但是,在您的情况下,您将这个new_node 保存在head 中,所以它不会丢失。完成后,您可以将new_node(它只是一个用于保存指针的变量!)用于其他目的。

      我建议将新节点的分配移至单独的函数:

      p_movie alloc_movie(char * name, float score)
      {
          p_movie new_node = malloc(sizeof(struct movie));
          if (new_node == NULL) {
              return NULL; // let the caller handle that
          }
          strcpy(new_node->movie_name, name);
          new_node->score = score;
          new_node->next = NULL;
          return new_node;
      }
      

      并将其与

      一起使用
      int main()
      {
          p_movie head = NULL;
          p_movie new_node = alloc_movie("Avatar", 9.5f);
          if (new_node == NULL)
          {
              exit(1);
          }
          // if (head == NULL) is unnecessary, as we can be sure it is null
          head = new_node;
          print_all(head);
      
          new_node = alloc_movie("Aladdin", 8.0f);
          if (new_node == NULL)
          {
              exit(1);
          }
      
          // Put the new node as the head, let the old head then be the next item.
          p_movie temp = head;
          head = new_node;
          new_node->next = temp;
      
          print_all(head);
      }
      

      甚至更短:

      p_movie alloc_movie(char * name, float score, p_movie next)
      {
          p_movie new_node = malloc(sizeof(struct movie));
          if (new_node == NULL) {
              return NULL; // let the caller handle that
          }
          strcpy(new_node->movie_name, name);
          new_node->score = score;
          new_node->next = next;
          return new_node;
      }
      
      int main()
      {
          p_movie new_node = alloc_movie("Avatar", 9.5f, NULL);
          if (new_node == NULL)
          {
              exit(1);
          }
          p_movie head = new_node;
          print_all(head);
      
          // Create a new node, with the old head as its "next".
          new_node = alloc_movie("Aladdin", 8.0f, head);
          if (new_node == NULL)
          {
              exit(1);
          }
      
          // Overwrite the head, as its old value is already saved in the "Aladdin" node.
          head = new_node;
          print_all(head);
      }
      

      【讨论】:

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