Menu Close

动态分配内存

在数组一章中,曾介绍过数组的长度是预先定义好的,在整个程序中固定不变。C语言中不允许动态数组类型。
例如:

int n;
scanf(“%d”,&n);
int a[n];

用变量表示长度,想对数组的大小作动态说明,这是错误的。但是在实际的编程中,往往会发生这种情况,即所需的内存空间取决于实际输入的数据,而无法预先确定。对于这种问题,用数组的办法很难解决。为了解决上述问题,C语言提供了一些内存管理函数,这些内存管理函数可以按需要动态地分配内存空间,也可把不再使用的空间回收待用,为有效地利用内存资源提供了手段。

常用的内存管理函数有以下三个:

1分配内存空间函数 malloc

调用形式:

ptr = (cast_type *) malloc (byte_size);

(类型说明符*) malloc (size)

功能:在内存的动态存储区中分配一块长度为”size”字节的连续区域。函数的返回值为该区域的首地址。

“类型说明符”表示把该区域用于何种数据类型。

(类型说明符*)表示把返回值强制转换为该类型指针。

“size”是一个无符号数。
例如:

pc=(char *)malloc(100);

表示分配 100 个字节的内存空间,并强制转换为字符数组类型,函数的返回值为指向该字符数组的指针,把该指针赋予指针变量 pc。

2. 分配内存空间函数 calloc

calloc 也用于分配内存空间。

调用形式:
ptr = (cast_type *) calloc (n, size);
(类型说明符*)calloc(n,size)

功能:

  1. 在内存动态存储区中分配 n 块长度为“size”字节的连续区域;
  2. 函数的返回值为该区域的首地址;
  3. 当内存空间分配后,所有字节初始化为零

每当分配内存空间错误(例如内存不足)时,都会返回空指针。

calloc 函数与 malloc 函数的区别仅在于一次可以分配 n 块区域。
例如:
ps=(struet stu*)calloc(2,sizeof(struct stu));

其中的 sizeof(struct stu)是求 stu 的结构长度。因此该语句的意思是:按 stu 的长度分配 2 块连续区域,强制转换为 stu 类型,并把其首地址赋予指针变量 ps。

3. 释放内存空间函数 free

调用形式:
free(void*ptr);

功能:释放 ptr 所指向的一块内存空间,ptr 是一个任意类型的指针变量,它指向被释放区域的首地址。被释放区应是由 malloc 或 calloc 函数所分配的区域。

例一: 分配一块区域,输入一个学生数据。

main()
{
 struct stu
 {
    int num;
    char *name;
    char sex;
    float score;
 } *ps;

   ps=(struct stu*)malloc(sizeof(struct stu));
   ps->num=102;
   ps->name="Zhang ping";
   ps->sex='M';
   ps->score=62.5;
   printf("Number=%d\nName=%s\n",ps->num,ps->name);
   printf("Sex=%c\nScore=%f\n",ps->sex,ps->score);
   free(ps);
}

 

本例中,定义了结构 stu,定义了 stu 类型指针变量 ps。然后分配一块 stu 大内存区,并把首地址赋予 ps,使 ps 指向该区域。再以 ps 为指向结构的指针变量对各成员赋值,并用
printf 输出各成员值。最后用 free 函数释放 ps 指向的内存空间。整个程序包含了申请内存空间、使用内存空间、释放内存空间三个步骤,实现存储空间的动态分配。

例二:

#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));
    }
}

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

例三:

#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);
}

例四:


#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;
    }

calloc与malloc:主要区别

calloc函数通常比malloc函数更合适,更高效。 虽然这两个函数都用于分配内存空间,但calloc可以一次分配多个块。 您不必每次都请求存储块。 calloc函数用于需要更大存储空间的复杂数据结构中。calloc函数分配的内存块始终初始化为零,而在malloc中,它始终包含垃圾值。

4. Realloc 函数

使用realloc()函数,可以将更多的内存大小添加到已分配的内存中。 它扩展当前块,同时保留原始内容。 realloc代表内存的重新分配。

realloc也可以用来减小先前分配的内存的大小。

语法:

ptr = realloc (ptr,newsize);

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

例五:

#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;
}

每当重新分配导致操作失败时,它都会返回空指针,并且先前的数据也将被释放。

动态数组

动态数组允许元素的数量根据需要增加。 它们被广泛用于计算机科学算法中。在以下程序中,我们动态创建并调整数组的大小.
例六

#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);
    }

动态内存分配是根据您的编程需求手动分配和释放内存。 动态内存是通过指针进行管理和服务的,这些指针指向我们称为堆的区域中新分配的内存空间。

您可以在运行时动态创建和销毁元素数组,而不会出现任何问题。 综上所述,自动内存管理使用栈(stack),而动态内存分配使用堆(Heap)。

<stdlib.h>库具有负责动态内存管理的函数:

这些函数可以在 <stdlib.h> 头文件中找到。

序号 函数和描述
1 void *calloc(int num, int size);
在内存中动态地分配 num 个长度为 size 的连续空间,并将每一个字节都初始化为 0。所以它的结果是分配了 num*size 个字节长度的内存空间,并且每个字节的值都是0。
2 void free(void *address);
该函数释放 address 所指向的内存块,释放的是动态分配的内存空间。
3 void *malloc(int num);
在堆区分配一块指定大小的内存空间,用来存放数据。这块内存空间在函数执行完成后不会被初始化,它们的值是未知的。
4 void *realloc(void *address, int newsize);
该函数重新分配内存,把内存扩展到 newsize

注意:void * 类型表示未确定类型的指针。C、C++ 规定 void * 类型可以通过类型转换强制转换为任何其它类型的指针。

摘要:

  1. 我们可以通过根据需要在堆中创建内存块来动态管理内存;
  2. 在动态内存分配中,内存是在运行时分配的;
  3. 动态内存分配允许操作大小灵活的字符串和数组,并且可以在程序中随时更改它们;
  4. 当您不知道某个特定结构要占用多少内存时,就需要这样做;
  5. Malloc是一个动态内存分配函数,代表内存分配,该内存分配将具有特定大小的内存块初始化为垃圾值;
  6. Calloc是连续的内存分配函数,可一次将多个内存块分配为0;
  7. Realloc用于根据指定的大小重新分配内存;
  8. Free释放功能用于清除动态分配的内存。