【问题标题】:What are all the ways to allocate memory in C and how do they differ?在 C 中分配内存的所有方法是什么,它们有何不同?
【发布时间】:2010-09-24 21:55:11
【问题描述】:

我知道以下几点:

  • malloc
  • 卡洛克
  • 重新分配

它们之间有什么区别?为什么 malloc 似乎几乎完全被使用?编译器之间是否存在行为差异?

【问题讨论】:

标签: c dynamic-memory-allocation


【解决方案1】:

malloc 分配内存。内存的内容保持原样(填充之前的内容)。

calloc 分配内存并将其内容设置为全零。

realloc 更改现有已分配内存块的大小,或将现有内存块的内容复制到新分配的请求大小的块中,然后释放旧块。

显然,realloc 是一种特殊情况。如果您没有要调整大小(或复制和解除分配)的旧内存块,则没有理由使用它。通常使用malloc 而不是calloc 的原因是因为将内存设置为全零会产生运行时成本,并且如果您打算立即用有用的数据填充内存(这很常见) ,首先将其归零是没有意义的。

这些函数都是标准的,并且可以在编译器中可靠地运行。

【讨论】:

  • calloc 对于分配结构数组特别有用,因为它需要 2 个参数:calloc(num_of_items, size_of_item)。用有用的数据填充这样的数组需要更多的工作(正如 Tyler 所说),因此有一个公平的论点支持将内存初始化为已知(零)值的额外成本。
  • calloc 还具有为您执行乘法的优势,因此在没有错误的实现中,它会检查并在溢出时失败。
  • 你没有提到strdupasprintf,它们也分配内存。
【解决方案2】:

除了你提到的那些(有些是扩展):

  • 堆栈上的变量也是动态分配的。递归是一种分配和使用它的方法。
  • C99 具有可变长度数组(正如 BlueRaja 提到的那样)。
  • 在某些系统上,您甚至有一个 alloca 调用,可让您在堆栈帧中分配可变长度的块。
  • POSIX 具有内存段和文件的映射,结合了shm_openopenmmap
  • SysV IPC 有 shmget 等调用

【讨论】:

  • Windows 也有办法在进程之间共享内存并将文件映射到内存中。
【解决方案3】:

尚未提及的一种分配内存的方法是alloca(size_t size),它在当前堆栈帧上保留 size 个字节的内存,并在您离开堆栈帧时再次自动释放内存。

【讨论】:

    【解决方案4】:

    calloc 可能只是实现为类似于:

    void * calloc(size_t nmemb, size_t size) {
          size_t len = nmemb * size);
          void * ptr = malloc(len);
          if (ptr) {
              return memset(ptr, 0, len);
          }
          return ptr;
    }
    

    所以它只是在 malloc 之前添加一个乘法,在 malloc 之后添加一个清除。

    malloc 可以(但可能永远不会)实现为:

    void * malloc(size_t size) {
          return realloc(NULL, size);
    }
    

    因为你可以传递 realloc 一个 NULL 指针作为前一个指针,但是 malloc 很可能不会这样实现,因为它会减慢所有 malloc 并且因为 realloc 可能使用 malloc。

    关于 realloc 的主要了解是它通常能够确定任何堆分配例程返回的内存块的实际大小,并查看该块是否已经足够大,或者在某些情况下是否足够大最好尽量缩小或移动它。

    void realloc(void * ptr, size_t size) {
        size_t alen = MALLOC_LENGTH_OF(ptr); // this just stands for some method of determining
                                         // the size of the block that was allocated, and could
                                         // be looking it up in a table or looking at a place
                                         // several bytes before the pointer, or some other
                                         // implementation specific thing
        if (0 == size) {
           free(ptr);
           return NULL;
        }
        if (alen >= size) {
            return ptr; // big enough, and not worrying about it being too big
        }
        void new_ptr = malloc(size); // since I said that malloc most likely isn't
                                    // implemented using realloc using malloc here is ok
        if (new_ptr && ptr) {
           memcpy(new_ptr, ptr, alen);
        }
        free(ptr);
        return new_ptr;
    }
    

    Malloc 使用较多,因为它最简单。程序员通常可以使他们的分配足够大,这样他们就不必担心使用 realloc 调整它们的大小,而且通常不需要清除内存。

    不同的库之间有不同的行为,您甚至可以将程序与其他版本链接以获得这种不同的行为,而无需(通常不更改编译器)。

    一些 malloc 库用于调试和错误检测。其他人可能会提供更好的性能。有些针对同时为多个不同线程分配内存进行了优化,避免了不同线程需要锁定整个堆来执行分配或释放。

    不过,从应用程序的角度来看,它们都应该提供相同的语义。

    【讨论】:

    • 投票反对在与块相同的行上使用开始花括号! (不是真的)
    • @Firoso:这样做会使 diff 在比较版本时产生更好的输出,并缩小代码。由于我还在一行块周围使用花括号,因此将它们与块打开代码放在同一行确实很有帮助。
    • 如果您参考 diff -p 选项,我相信启发式是如果一行在第 1 列有字母,则该行包含函数名称。
    • 正如 R. 在其他地方所说,许多实现(包括 glibc、FreeBSD libc 和 HP-UX libc IIRC)上的 calloc() 会检查整数溢出,以避免潜在的安全漏洞。
    【解决方案5】:

    ma​​lloc: 假设你有 ptr=(int *)malloc(10); 这会分配 10 个连续字节的内存空间,第一个字节的地址是存储在指针变量ptr中。分配的内存现在包含垃圾值。
    因此,如果 i 从 0 变化到 3 scanf("%d",ptr+i); 在 4 个连续位置存储 4 个整数。 ptr 具有第一个整数的地址,ptr+1 具有第二个数字的地址,依此类推。 因此 printf("%d",atstrick(ptr+i)); 将打印相应的值。 与为变量和数组分配的内存不同,动态分配的内存没有与之关联的名称。如上所示。

    calloc: 与 malloc 类似,但有两点不同: 1. 声明: ptr=(int *)calloc(5,sizeof(int));这里我们有两个参数,5 是没有分配的块,第二个参数等于 4 个字节。这相当于 ptr=(int *)malloc(5*sizeof(int)); 2。在 calloc 中,最初分配的内存不是垃圾,而是 0。如果堆中没有足够的可用内存,malloc 和 calloc 都返回 NULL。

    【讨论】:

      猜你喜欢
      • 2014-11-10
      • 1970-01-01
      • 2011-08-09
      • 1970-01-01
      • 1970-01-01
      • 2012-10-30
      • 2010-11-17
      • 2018-11-09
      • 1970-01-01
      相关资源
      最近更新 更多