使用 malloc()、calloc() 函数在 C 中进行动态内存分配

在学习C语言动态内存分配之前,让我们先了解一下

C语言中的内存管理是如何工作的?

当您使用基本数据类型声明一个变量时,C编译器会自动为该变量在称为堆栈的内存池中分配内存空间。

例如,一个float变量在声明时通常占用4个字节(取决于平台)。我们可以使用sizeof运算符来验证此信息,如下面的示例所示

#include <stdio.h>
int main() { float x; printf("The size of float is %d bytes", sizeof(x)); return 0;}

输出将是

 The size of float is 4 bytes

同样,一个指定大小的数组在内存中被分配为连续的块,每个块的大小为一个元素

#include <stdio.h>
int main() { float arr[10];
printf("The size of the float array with 10 element is %d", sizeof(arr)); return 0;}

结果是

 The size of the float array with 10 element is 40

正如我们到目前为止所学的,在声明基本数据类型或数组时,内存是自动管理的。然而,C语言中有一个分配内存的过程,它允许您实现一个数组大小在程序运行时(runtime)才能确定的程序。这个过程称为“动态内存分配”。

C语言中的动态内存分配

动态内存分配是根据您的编程需求手动分配和释放内存。动态内存由指针管理和提供,这些指针指向我们称为堆(heap)的区域中新分配的内存空间。

现在您可以轻松地在运行时创建和销毁任意大小的元素数组。总而言之,自动内存管理使用堆栈,而C语言的动态内存分配使用堆。

<stdlib.h>库中包含了负责动态内存管理的函数。

函数 目的
malloc() 分配请求大小的内存,并返回指向分配空间的第一个字节的指针。
已分配空间。
calloc() 为数组元素分配空间。将元素初始化为零,并返回指向内存的指针。
realloc() 用于修改先前分配的内存空间的大小。
Free() 释放或取消分配先前分配的内存空间。

让我们通过应用来讨论上述函数。

C语言中的malloc()函数

C语言的malloc()函数代表内存分配。它是一个用于动态分配内存块的函数。它保留指定大小的内存空间,并返回指向内存位置的空指针。返回的指针通常是void类型。这意味着我们可以将C语言的malloc()函数分配给任何指针。

malloc() 函数的语法

ptr = (cast_type *) malloc (byte_size);

此处,

  • ptr是cast_type的指针。
  • C语言的malloc()函数返回指向已分配内存的byte_size的指针。

malloc() 的示例

Example: ptr = (int *) malloc (50)

当此语句成功执行时,将保留50个字节的内存空间。保留空间的第一个字节的地址被分配给int类型的指针ptr。

考虑另一个例子

#include <stdlib.h>
int main(){
int *ptr;
ptr = malloc(15 * sizeof(*ptr)); /* a block of 15 integers */
    if (ptr != NULL) {
      *(ptr + 5) = 480; /* assign 480 to sixth integer */
      printf("Value of the 6th integer is %d",*(ptr + 5));
    }
}

输出

Value of the 6th integer is 480

C malloc() Function

  1. 请注意,此处使用了sizeof(*ptr)而不是sizeof(int),以使代码在*ptr声明后来被类型转换为其他数据类型时更加健壮。
  2. 如果内存不足,分配可能会失败。在这种情况下,它会返回NULL指针。因此,您应该包含代码来检查NULL指针。
  3. 请记住,分配的内存是连续的,并且可以被视为一个数组。我们可以使用指针算术来访问数组元素,而不是使用方括号[]。我们建议使用+来引用数组元素,因为使用递增++或+=会改变指针存储的地址。

malloc() 函数也可与字符数据类型以及结构等复杂数据类型一起使用。

C语言中的free()函数

变量分配的内存会在编译时自动取消分配。在动态内存分配中,您必须显式地取消分配内存。如果这样做,您可能会遇到内存不足的错误。

free()函数用于在C语言中释放/取消分配内存。通过在程序中释放内存,您可以使更多内存可供以后使用。

例如

#include <stdio.h>
int main() {
int* ptr = malloc(10 * sizeof(*ptr));
if (ptr != NULL){
  *(ptr + 2) = 50;
  printf("Value of the 2nd integer is %d",*(ptr + 2));
}
free(ptr);
}

输出

 Value of the 2nd integer is 50

C语言中的calloc()函数

C语言的calloc()函数代表连续分配。此函数用于分配多个内存块。它是一个动态内存分配函数,用于为数组和结构等复杂数据结构分配内存。

malloc() 函数用于分配单个内存块,而C语言的calloc()用于分配多个内存块。calloc()函数分配的每个块大小都相同。

calloc() 函数的语法

ptr = (cast_type *) calloc (n, size);
  • 上述语句用于分配n个相同大小的内存块。
  • 在内存空间分配之后,所有字节都将初始化为零。
  • 返回当前指向已分配内存空间第一个字节的指针。

每当发生内存分配错误(如内存短缺)时,将返回空指针。

calloc() 的示例

下面的程序计算算术序列的和。

#include <stdio.h>
    int main() {
        int i, * ptr, sum = 0;
        ptr = calloc(10, sizeof(int));
        if (ptr == NULL) {
            printf("Error! memory not allocated.");
            exit(0);
        }
        printf("Building and calculating the sequence sum of the first 10 terms \ n ");
        for (i = 0; i < 10; ++i) { * (ptr + i) = i;
            sum += * (ptr + i);
        }
        printf("Sum = %d", sum);
        free(ptr);
        return 0;
    }

结果

Building and calculating the sequence sum of the first 10 terms
Sum = 45

calloc() vs. malloc():主要区别

以下是C语言中malloc() 与 calloc()之间的主要区别

calloc() 函数通常比malloc() 函数更适合且更高效。虽然这两个函数都用于分配内存空间,但calloc() 可以一次分配多个块。您不必每次都请求一个内存块。calloc() 函数用于需要更大内存空间的复杂数据结构。

calloc() 在C语言中分配的内存块始终初始化为零,而在malloc() 函数中,它始终包含垃圾值。

C语言中的realloc()函数

使用C语言的realloc()函数,您可以为已分配的内存添加更多内存大小。它扩展了当前块,同时保持原始内容不变。C语言的realloc()代表内存重新分配。

realloc() 也可用于减小先前分配的内存大小。

realloc() 函数的语法

ptr = realloc (ptr,newsize);

上述语句在变量newsize中分配具有指定大小的新内存空间。执行函数后,指针将返回到内存块的第一个字节。新大小可以大于或小于先前内存。我们不能确定新分配的块是否会指向与先前内存块相同的位置。此函数会将所有先前的数据复制到新区域。它确保数据安全。

realloc() 的示例

#include <stdio.h>
int main () {
   char *ptr;
   ptr = (char *) malloc(10);
   strcpy(ptr, "Programming");
   printf(" %s,  Address = %u\n", ptr, ptr);

   ptr = (char *) realloc(ptr, 20); //ptr is reallocated with new size
   strcat(ptr, " In 'C'");
   printf(" %s,  Address = %u\n", ptr, ptr);
   free(ptr);
   return 0;
}

每当C语言的realloc() 操作不成功时,它会返回一个空指针,并且先前的数据也会被释放。

C语言中的动态数组

C语言中的动态数组允许元素数量根据需要增长。C语言的动态数组广泛用于计算机科学算法。

在以下程序中,我们在C中创建并调整了动态数组的大小。

#include <stdio.h>
    int main() {
        int * arr_dynamic = NULL;
        int elements = 2, i;
        arr_dynamic = calloc(elements, sizeof(int)); //Array with 2 integer blocks
        for (i = 0; i < elements; i++) arr_dynamic[i] = i;
        for (i = 0; i < elements; i++) printf("arr_dynamic[%d]=%d\n", i, arr_dynamic[i]);
        elements = 4;
        arr_dynamic = realloc(arr_dynamic, elements * sizeof(int)); //reallocate 4 elements
        printf("After realloc\n");
        for (i = 2; i < elements; i++) arr_dynamic[i] = i;
        for (i = 0; i < elements; i++) printf("arr_dynamic[%d]=%d\n", i, arr_dynamic[i]);
        free(arr_dynamic);
    }

C语言动态数组程序在屏幕上的结果

 
arr_dynamic[0]=0
arr_dynamic[1]=1
After realloc
arr_dynamic[0]=0
arr_dynamic[1]=1
arr_dynamic[2]=2
arr_dynamic[3]=3

摘要

  • 我们可以通过在堆中按需创建内存块来动态管理内存。
  • 在C语言动态内存分配中,内存是在运行时分配的。
  • 动态内存分配允许您处理大小灵活且可以在程序中随时更改的字符串和数组。
  • 当您不知道某个特定结构将占用多少内存时,就需要它。
  • C语言的Malloc()是一个动态内存分配函数,代表内存分配,它分配具有特定大小并初始化为垃圾值的内存块。
  • C语言的Calloc()是一个连续内存分配函数,它一次分配多个内存块并初始化为0。
  • C语言的Realloc()用于根据指定大小重新分配内存。
  • Free()函数用于清除动态分配的内存。