6.内存管理(六)
C语言支持动态存储分配,即在程序执行期间分配内存单元的能力,利用动态存储分配,可以设计出能够根据需要扩大和缩小的数据结构。
C语言为内存的分配和管理提供了几个函数。这些函数可以在
- void calloc(int num, int size):
在内存中动态地分配num个长度为size的连续空间,并将每一个字节都初始化为0。所以它的结果是分配了numsize个字节长度的内存空间,并且每个字节的值都是0。 - void free(void *address):
该函数释放address所指向的内存块,释放的是动态分配的内存空间。 - void *malloc(int num):
在堆区分配一块指定大小的内存空间,用来存放数据。这块内存空间在函数执行完成后不会被初始化,它们的值是未知的。当分配成功时,它返回一个指针,该指针指向n字节长度的未初始化的存储空间,否则返回NULL。 - void *realloc(void *address, int newsize):
该函数重新分配内存,把内存扩展到newsize。
注意:void * 类型表示未确定类型的指针。
C、C++ 规定void *类型可以通过类型转换强制转换为任何其它类型的指针。
这是因为在为了申请内存块而调用内存分配函数时,由于函数无法知道计算存储在内存块中的数据是什么类型,所以它不能返回int类型、char类型等普通类型的指针。
取而代之的,函数会返回void *类型的值。void *类型的值是通用指针,它本质上是内存地址。
动态分配内存
编程时,如果您预先知道数组的大小,那么定义数组时就比较容易。例如,一个存储人名的数组,它最多容纳100个字符,所以您可以定义数组,如下所示:
char name[100];
但是,如果您预先不知道需要存储的文本长度,例如您想存储有关一个主题的详细描述。在这里,我们需要定义一个指针,该指针指向未定义所需内存大小的字符,后续再根据需求来分配内存,如下所示:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
char name[100];
char *description;
strcpy(name, "Zara Ali");
// 动态分配内存
description = (char *)malloc( 200 * sizeof(char));
if (description == NULL) {
fprintf(stderr, "Error - unable to allocate required memory\n");
} else {
strcpy( description, "Zara ali a DPS student in class 10th");
}
printf("Name = %s\n", name);
printf("Description: %s\n", description);
}
执行结果:
Name = Zara Ali
Description: Zara ali a DPS student in class 10th
上面的程序也可以使用calloc()来编写,只需要把malloc替换为calloc即可,如下所示:
calloc(200, sizeof(char));
当动态分配内存时,您有完全控制权,可以传递任何大小的值。而那些预先定义了大小的数组,一旦定义则无法改变大小。
malloc()函数接受一个参数:所需的内存字节数。
malloc()函数会找到合适的空闲内存块,这样的的内存是匿名的。
也就是说,malloc()分配内存,但是不会为其赋名。然而它确实返回动态分配内存块的首字节地址。因此,可以把该地址赋给一个指针变量,并使用指针访问这块内存。
重新调整内存的大小和释放内存
当程序退出时,操作系统会自动释放所有分配给程序的内存,但是,建议您在不需要内存时,都应该调用函数free()来释放内存。
或者,您可以通过调用函数realloc()来增加或减少已分配的内存块的大小。让我们使用realloc()和free()函数,再次查看上面的实例:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
char name[100];
char *description;
strcpy(name, "Zara Ali");
/* 动态分配内存 */
description = (char *)malloc( 30 * sizeof(char));
if ( description == NULL) {
fprintf(stderr, "Error - unable to allocate required memory\n");
} else {
strcpy( description, "Zara ali a DPS student.");
}
/* 假设您想要存储更大的描述信息 */
description = realloc( description, 100 * sizeof(char));
if (description == NULL) {
fprintf(stderr, "Error - unable to allocate required memory\n");
} else {
strcat( description, "She is in class 10th");
}
printf("Name = %s\n", name);
printf("Description: %s\n", description);
// 使用 free() 函数释放内存
free(description);
}
执行结果:
Name = Zara Ali
Description: Zara ali a DPS student.She is in class 10th
空指针
当调用内存分配函数时,总存在这样的可能性。找不到满足我们需要的足够大的内存块,如果真的发生了这类问题,函数会返回空指针(null pointer)。空指针是不指向任何地方的指针。空指针用名为NULL的宏来表示。
在C语言中,指针测试真假的方法和数的测试一样。所有非空指针都为真,而只有空指针为假。因此
if (p == NULL)可以写成if (!p)
内存泄露
malloc函数和其他内存分配函数所获得的内存块都来自一个称为堆(heap)的存储池。过于频繁的调用这些函数(或者让函数申请大内存块)可能会耗尽堆,这会导致函数返回空指针。更糟的是,程序可能分配了内存块,然后又丢失了对这些块的记录,因此浪费了空间。例如:
```; p = malloc(...); q = malloc(...); p = q;
在执行完前两句语句后,p指向了一个内存块,而q指向了另一个内存块。
在把q赋值给p之后,两个指针现在都指向了第二个内存块。因为没有指针指向第一个内存块,所以再也不能使用此内存块了。
对程序而言,不可再访问到的内存块成为垃圾(garbage)。留有垃圾的程序存在内存泄露(memory leak)现象。一些语言提供垃圾收集器(garbage collector)用于垃圾的自动定位和回收,但是C语言不提供。相反,每个C程序负责回收各自的垃圾,方法是调用free函数来释放不需要的内存:
```c
p = malloc(...);
q = malloc(...);
free(p);
p = q;
- 邮箱 :charon.chui@gmail.com
- Good Luck!