【问题标题】:malloc vs realloc - what is the best practice?malloc vs realloc - 最佳实践是什么?
【发布时间】:2021-12-27 10:09:46
【问题描述】:

我有一个变量,其大小在运行时确定。

每次添加新元素时realloc 是否更好,例如:

array_type * someArray;
int counter = 0;

//before a new element is added:
counter ++;
someArray = realloc(someArray, counter * sizeof(array_type));

或者使用malloc 一次分配比可能需要更多的内存:

array_type * someArray = malloc(ENOUG_MEMORY * sizeof(array_type));

效率(速度)可读性内存管理方面什么是最好的?为什么?

【问题讨论】:

  • 我认为这取决于用例。
  • 另一种选择是在每次“满”时按某个选定的百分比或选定的元素数量增长它,我们需要添加它。
  • 通常内存(重新)分配是一个繁重的过程(因为如果本地池较低,可能会涉及操作系统)。标准技术是为每次扩展定义一块内存来分配,以这种方式限制对系统资源的访问。当然,如果重新分配应该在流程开始时发生或数量有限,您可以决定继续进行单次重新分配。与往常一样,这取决于上下文。附:在一些小型或嵌入式系统上,内存分配会影响整个系统的性能或导致内存不足。在这些情况下,策略是强制的

标签: c memory memory-management malloc realloc


【解决方案1】:

realloc 在过去可能偶尔有用,在单线程代码中具有某些分配模式。大多数现代内存管理器都针对多线程程序进行了优化并最大限度地减少碎片,因此,当增加分配时,realloc 几乎肯定会分配一个新块,复制现有数据,然后释放旧块。所以尝试使用realloc并没有真正的优势。

一次增加一个元素的大小会产生 O(n^2) 的情况。在最坏的情况下,每次都必须复制现有数据。最好以块为单位增加大小(仍然是 O(n^2),但具有更小的常数因子)或以几何方式增加分配(这会产生 O(n) 摊销成本)。

而且,realloc 的正确使用也很困难。

someArray = realloc(someArray, counter * sizeof(array_type));

如果realloc 失败,someArray 将设置为NULL。如果那是指向先前分配的内存的指针的唯一副本,那么您刚刚丢失了它。 您将无法访问已放置的数据,也无法释放原始分配,因此会发生内存泄漏。

【讨论】:

    【解决方案2】:

    在效率(速度)、可读性和内存管理方面什么是最好的?为什么?

    没有一般最好的。具体最好取决于您的特定应用程序和用例以及环境。您可以对perfect realloc ratio 发起战争并决定if you need realloc at all

    记住rules of optimization。你不优化。然后,你不优化,没有先测量。可以根据您的特定设置、特定环境、使用特定操作系统和 *alloc 实现的特定应用程序来衡量任何方面的最佳表现。

    最佳做法是什么?

    分配恒定数量的内存(如果足够小)是静态的。这只是一个数组。将您的应用程序重构为:

    array_type someArray[ENOUGH_MEMORY];
    

    如果您不想过度分配(或ENOUGH_MEMORY 足够大),则使用realloc 添加一个元素,如图所示。

    如果您愿意,可以通过不经常调用 realloc 来“优化”并且过度分配 - 似乎比率 1.5 是上面链接线程中最优选的。不过,它高度是特定于应用程序的 - 我会在 Linux 上过度分配,而在 STM32 或其他裸机上工作时不会。

    【讨论】:

      【解决方案3】:

      我会谨慎使用realloc。 调用realloc 通常会导致:

      1. 分配全新的区块
      2. 将所有数据从旧位置复制到新位置
      3. 释放(释放)初始块。

      根据应用、数据量、响应要求,从性能角度来看,所有组合都可能存在问题。

      另外,在realloc失败的情况下,返回值为NULL,这意味着分配到新块不是直接的(需要间接)。例如

          int *p = malloc(100 * sizeof *p);
          if (NULL == p) 
          {
              perror("malloc() failed");
              return EXIT_FAILURE;
          }
       
          do_something_with_p(p);
      
          /* Reallocate array to a new size
           * Using temporary pointer in case realloc() fails. */
          {
              int *temp = realloc(p, 100000 * sizeof *temp);
      
              if (NULL == temp)
              {
                  perror("realloc() failed");
                  free(p);
                  return EXIT_FAILURE;
              }      
      
              p = temp;
          }
      

      【讨论】:

        【解决方案4】:

        malloc 与 realloc - 最佳实践是什么?

        辅助函数

        在编写健壮的代码时,我避免直接​​使用库*alloc() 函数。相反,我形成辅助函数来处理各种用例并处理边缘情况、参数验证等。

        在这些辅助函数中,我使用 malloc(), realloc(), calloc() 作为构建块,可能由实现宏控制,以根据用例形成良好的代码。

        这将“什么是最好的”推向了一组更窄的条件,在这些条件下可以更好地评估 - 每个功能。在增长 2 倍的情况下,realloc() 很好。

        例子:

        // Optimize for a growing allocation
        // Return new pointer.
        // Allocate per 2x *nmemb * size.
        // Update *nmemb_new as needed.
        // A return of NULL implies failure, old not deallocated.
        void *my_alloc_grow(void *ptr, size_t *nmemb, size_t size) {
          if (nmemb == NULL) {
            return NULL;
          }
          size_t nmemb_old = *nmemb;
        
          if (size == 0) { // Consider array elements of size 0 as error
            return NULL;
          }
          if (nmemb_old > SIZE_MAX/2/size)) {
            return NULL;
          }
        
          size_t nmemb_new = nmemb_old ? (nmemb_old * 2) : 1;
          unsigned char *ptr_new = realloc(ptr, nmemb_new * size);
          if (ptr_new == NULL) {
            return NULL;
          }
        
          // Maybe zero fill new memory portion.
          memset(ptr_new + nmemb_old * size, 0, (nmemb_new - nmemb_old) * size);
        
          *nmemb = nmemb_new;
          return ptr_new;
        }
        

        其他用例。

        / General new memory
        void *my_alloc(size_t *nmemb, size_t size); // General new memory
        void *my_calloc(size_t *nmemb, size_t size); // General new memory with zeroing
        
        // General reallocation, maybe more or less.
        // Act like free() on nmemb_new == 0.
        void *my_alloc_resize(void *ptr, size_t *nmemb, size_t nmemb_new, size_t size);
        

        【讨论】:

          猜你喜欢
          • 2011-06-07
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2023-03-04
          相关资源
          最近更新 更多