在数组一章中,曾介绍过数组的长度是预先定义好的,在整个程序中固定不变。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)
功能:
- 在内存动态存储区中分配 n 块长度为“size”字节的连续区域;
- 函数的返回值为该区域的首地址;
- 当内存空间分配后,所有字节初始化为零
每当分配内存空间错误(例如内存不足)时,都会返回空指针。
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)); } }
- 请注意,当以后将* ptr声明类型转换为其他数据类型时,使用sizeof(* ptr)代替sizeof(int)是为了使代码更有兼容性;
- 如果内存不足,分配可能会失败。 在这种情况下,它将返回NULL指针。 因此,您应该包括用于检查NULL指针的代码;
- 请记住,分配的内存是连续的,可以将其视为数组。 我们可以使用指针算法来访问数组元素,而不是使用方括号[]。 我们建议使用+来引用数组元素,因为使用增量++或+ =会更改指针存储的地址。
例三:
#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 * 类型可以通过类型转换强制转换为任何其它类型的指针。
摘要:
- 我们可以通过根据需要在堆中创建内存块来动态管理内存;
- 在动态内存分配中,内存是在运行时分配的;
- 动态内存分配允许操作大小灵活的字符串和数组,并且可以在程序中随时更改它们;
- 当您不知道某个特定结构要占用多少内存时,就需要这样做;
- Malloc是一个动态内存分配函数,代表内存分配,该内存分配将具有特定大小的内存块初始化为垃圾值;
- Calloc是连续的内存分配函数,可一次将多个内存块分配为0;
- Realloc用于根据指定的大小重新分配内存;
- Free释放功能用于清除动态分配的内存。