栈上的数组与局部变量大小通常在编译期或进入作用域时即已确定。若程序在运行时才确定所需数据量(编写时尚不确定大小),比如用户输入要存放多少个整数、或根据文件中的记录条数决定缓冲区长度,可使用动态内存分配:在堆(heap)上按字节数申请存储,用指针保存返回地址,使用完毕后调用 free 归还。
为何需要动态分配
- 元素个数或结构体数量可由用户输入、文件内容等运行时信息决定。
- 生命周期可跨越函数调用,在不再需要时显式释放,避免长期占用。
- 为链表、动态表格等“边用边扩”的结构提供存储基础。
malloc 与 free
以 person 结构体为例:
typedef struct {
char *name;
int age;
} person;#include <stdlib.h>
person *myperson = (person *)malloc(sizeof(person));malloc(sizeof(person))申请可容纳一个person的连续字节,成功时返回指向该区域的指针。malloc的返回类型为void *,常转换为person *后再使用。sizeof(person)在编译期求得结构体所占字节数,用于指定申请大小。
myperson->name = "John";
myperson->age = 27;
printf("%s,%d 岁\n", myperson->name, myperson->age);
free(myperson);
myperson = NULL;free 释放的是指针所指的堆内存,而非指针变量本身。释放后若继续通过原指针访问,可能形成悬垂指针;将指针置为 NULL 有助于避免误用。
检查分配是否成功
person *p = (person *)malloc(sizeof(person));
if (p == NULL) {
printf("内存分配失败!\n");
return 1;
}内存不足时 malloc 返回 NULL,使用前应判空。
动态分配一维数组
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int n = 5;
char *pvowels = (char *)malloc(n * sizeof(char));
if (pvowels == NULL) {
return 1;
}
pvowels[0] = 'A';
pvowels[1] = 'E';
*(pvowels + 2) = 'I';
pvowels[3] = 'O';
*(pvowels + 4) = 'U';
for (int i = 0; i < n; i++) {
printf("%c ", pvowels[i]);
}
printf("\n");
free(pvowels);
return 0;
}pvowels 是指向堆上连续 char 的指针,可用下标或指针算术访问,用法类似数组,但须自行记录长度并在适当时机 free。
动态分配二维结构
行数、列数在运行时确定时,可先分配“行指针数组”,再为每一行分配列存储:
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int nrows = 2, ncols = 5;
char **pvowels = (char **)malloc(nrows * sizeof(char *));
if (pvowels == NULL) {
return 1;
}
for (int i = 0; i < nrows; i++) {
pvowels[i] = (char *)malloc(ncols * sizeof(char));
if (pvowels[i] == NULL) {
return 1;
}
}
pvowels[0][0] = 'A'; pvowels[0][1] = 'E'; pvowels[0][2] = 'I';
pvowels[0][3] = 'O'; pvowels[0][4] = 'U';
pvowels[1][0] = 'a'; pvowels[1][1] = 'e'; pvowels[1][2] = 'i';
pvowels[1][3] = 'o'; pvowels[1][4] = 'u';
for (int i = 0; i < nrows; i++) {
for (int j = 0; j < ncols; j++) {
printf("%c ", pvowels[i][j]);
}
printf("\n");
}
for (int i = 0; i < nrows; i++) {
free(pvowels[i]);
}
free(pvowels);
return 0;
}释放顺序应与分配相反:先释放各行,再释放行指针数组。
内存泄漏与配对释放
每次成功的 malloc(或 calloc、realloc)都应有对应的 free;遗漏释放会导致内存泄漏。同一指针不要重复 free。动态内存与指针配合使用时,更需关注边界与生命周期。
本节介绍了堆上分配与释放的基本流程。malloc 返回 void *,其含义见void 指针一节。