动态存储分配
C语言的数据结构通常是固定大小的,即使是变长数组,虽然其长度在运行时确定,但在其声明周期时依然是固定长度的。
动态存储分布即是在程序执行期间分配内存,可以根据需要扩大或缩小数据结构。
动态存储分布只要用于字符串、数组和结构,通常应用于构建表、树等其它数据结构。
内存分配函数
以内存声明函数通过
<stdlib.h>
获取。malloc
:分配内存块,但不对内存块进行初始化——更高效void *malloc(size_t size);
calloc
:分配内存块,并对内存块清零void *calloc(size_t num, size_t size);
realloc
:调整先前分配的内存块大小void *realloc(void *ptr, size_t size);
内存分配函数返回一个通用指针类型
void *
, 存储着指向内存块的指针。空指针:当分配失败时,函数返回空指针。需要认为检验指针变量是否为空:
ptr = malloc(1000); if (p == NULL) { /* 分配失败 */ }
动态分配字符串
使用
malloc
函数为字符串分配内存。C语言保证char类型需要一个字节的内存(
sizeof(char)
为1
)。给n
个字符的字符串分配内存空间可以写成:p = (char *) malloc(n + 1); // 为空字符\n分配空间
虽然在给
p
赋值时,原来的通用指针*void
会自动转化为对应的字符指针*char
。但这里我们进行强制的类型转换。对上述字符串进行初始化的一种方法:
#include <string.h> strcpy(p, "abc");
下面这个函数将接受两个字符串,在不改变原来的字符串的前提下,合并字符串并且存储起来。
#include <string.h> #include <stdlib.h> char *concat(const char *s1, const char *s2) { char *result; result = malloc(strlen(s1) + strlen(s2) + 1); if (result == NULL) { printf("Error: malloc failed in concat\n"); exit(EXIT_FAILURE); } strcpy(result, s1); strcat(result, s2); return result; }
动态分布数组
使用malloc
函数为数组分配存储空间
由于不确定每个元素所需要的内存空间,所以需要使用
sizeof()
函数计算。假设数组需要包含
n
个整数,则需要如此分配内存空间。int *a; a = malloc(n * sizeof(int));
一旦
a
指向动态分配的内存块,就可以忽略a
是指针的事实,可以把它当作数组名来用。
calloc
函数
此函数需要传入元素的数量和每个元素的长度。
int a; a = calloc(n, sizeof(int)) // 分配内存给n个整型变量
分配内存后会把所有位置设置为0.
通过此函数,为数据类型分配内存,并且初始化其元素为0.
struct point { int x; int y; } *p; p = calloc(1, sizeof(struct point)); // 为一个结构体分配内存
realloc
函数
改变已经动态分配的内存块的大小。
需要传入已分配内存块的指针,以及新的内存大小。
不会对添加的内存块字节进行初始化。
如果对被调用时以
0
为第二个实际参数,则函数将会释放内存块。
释放存储空间
频繁地调用内存分配函数,或者调用过大,有可能耗尽可分配空间。
分配了的内存空间,却丢失了对这些内存空间的记录,就会造成浪费:
p = malloc(...); p -> 内存块1 q = malloc(...); q -> 内存块2 --------------------------------- p = q; -> 内存块1 p, q -> 内存块2
使用
free
函数释放内存:void free(void *ptr);
只能对指向动态分配的内存块的指针使用
free
函数。以上代码的正确用法:
p = malloc(...); q = malloc(...); free(p); // 将p的内存释放掉 p = q; // 将p指向另外一个内存块
如果释放了内存,那么指向该内存的指针将成为悬空指针。此时使用该指针将导致严重错误。最好将该指针重置为空指针。