在C语言中,分配内存的所有方法是什么?它们有什么不同?

在C语言中,分配内存的所有方法是什么?它们有什么不同?,c,dynamic-memory-allocation,C,Dynamic Memory Allocation,我知道以下几点: 马洛克 卡洛克 realloc 它们之间有什么区别?为什么malloc看起来几乎是唯一被使用的?编译器之间是否存在行为差异?malloc分配内存。内存的内容保持原样(填充以前存在的内容) calloc分配内存并将其内容设置为全零 realloc更改现有已分配内存块的大小,或将现有内存块的内容复制到请求大小的新分配内存块中,然后取消分配旧块 显然,realloc是一种特殊情况。如果您没有旧的内存块来调整大小(或复制并释放),那么就没有理由使用它。通常使用malloc而不是ca

我知道以下几点:

  • 马洛克
  • 卡洛克
  • realloc

它们之间有什么区别?为什么malloc看起来几乎是唯一被使用的?编译器之间是否存在行为差异?

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

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

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

显然,
realloc
是一种特殊情况。如果您没有旧的内存块来调整大小(或复制并释放),那么就没有理由使用它。通常使用
malloc
而不是
calloc
的原因是,将内存设置为全零会带来运行时成本,如果您计划立即用有用的数据填充内存(这是很常见的),那么首先将其归零没有意义


这些函数都是标准函数,在编译器中运行可靠。

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之后加一个clear

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

void * malloc(size_t size) {
      return realloc(NULL, size);
}
因为您可以将一个空指针作为前一个指针传递给realloc,但是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库用于调试和错误检测。其他可能提供更好的性能。有些优化了同时为几个不同的线程分配内存,避免了不同的线程需要锁定整个堆来执行分配或释放


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

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

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

一种分配内存的方法没有提到,它是alloca(size\t size),它在当前堆栈帧上保留大小字节的内存,并在您离开堆栈帧时自动释放内存。

malloc:假设您有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

不要忘记:)
calloc
对于分配结构数组特别有用,因为它需要两个参数:
calloc(项目的数量、项目的大小)
。用有用的数据填充这样一个数组需要更多的工作(正如泰勒所说),因此有一个公平的理由支持将内存初始化为已知(零)值的额外成本。
calloc
还具有为您执行乘法的优势,因此,在无错误的实现中,它将检查溢出并失败。您没有提到
strdup
asprintf
,它们也会分配内存。投票反对在与块相同的行上使用开始大括号!(不是真的tho)@Firoso:这样做可以使diff在比较版本时产生更好的输出,同时可以缩减代码。由于我还在单行块周围使用大括号,因此将它们与块开头代码放在同一行确实很有帮助。如果您提到diff-p选项,我相信启发性的做法是,如果一行在第1列有字母,则该行包含函数名。正如R.在其他地方所说,在许多实现中使用calloc()(包括glibc、FreeBSD libc和HP-UX libc IIRC)检查整数溢出,以