使用动态内存分配创建数组,称为动态数组,便于存储未知长度的数据。
动态数组和变长数组不同,变长数组是指数组长度由变量控制的数组。
malloc
—— 分配内存块,但不进行初始化
calloc
—— 分配内存块(指定单元大小和个数),并将内存块清零
realloc
—— 调整先前分配的内存块的大小
-
malloc 函数最常用,不需要内存清零,更高效。
-
动态内存分配函数所在头文件为
<stdlib.h>
。
0、空指针
当找不到满足需要的足够大的内存块时,函数会返回空指针。
空指针(NULL pointer)是不指向任何地方的指针,在把函数的返回值存储到指针变量之后,需要检验指针变量是否为空指针。
(试图通过空指针访问内存是未定义行为,可能引起程序崩溃或其他不可预测的问题)
char *p;
if( ( p = (char*)malloc(100000) ) == NULL ){
printf("Allocation failed!\n");
}
else{
...
}
- 名为 NULL 的宏在<locale.h>、<stddef.h>、<stdio.h>、<stdlib.h>、<string.h>、<wchar.h>(C99) 中都有定义
C语言中,空指针为假,所有非空指针都为真,如下:
//等价
if(p == NULL) ...
if(!p) ...
//等价
if(p != NULL) ...
if(p) ...
1、malloc
函数
malloc —— memory allocation 内存分配
1.1 函数原型
void * malloc(size_t size);
1.2 功能
- 从内存中临时分配一块内存出来,每个单元为 size 个字节,长度不定;
- 分配的内存区域不会被初始化;
- 返回 void 型指针,指向所分配的内存的首地址。
- 无内存可供分配时,返回NULL。
1.3 调用方法
int* p = (int*)malloc(sizeof(int));
char* ch = (char*)malloc(sizeof(char));
1.4 注意事项
- 调用前需包含函数所在头文件,
#include <stdlib.h>
- 因为是临时分配的内存空间,使用完之后需要释放
free(p);
,或重新分配realloc
。 free
或realloc
函数的参数必须是最开始malloc
函数分配的内存区域的原始首地址。- 当需要为字符串分配内存空间时,不能忽略最后的空字符。
2、calloc
函数
calloc —— contiguous allocation 连续分配
2.1 函数原型
void * calloc(size_t n,size_t size);
2.2 功能
- 从内存中临时分配一块内存出来,每个单元为 size 个字节,长度为 n 。
- 分配的内存区域的所有位初始化为0。
- 返回void型指针,指向所分配的内存的首地址。
- 无内存可供分配时,返回NULL。
2.3 调用方法
int* p = (int*)calloc(n, sizeof(int));
char* ch = (char*)calloc(n, sizeof(char));
2.4 注意事项
- 调用前需包含函数所在头文件,
#include <stdlib.h>
- 因为是临时分配的内存空间,使用完之后需要释放
free(p);
,或重新分配realloc
。 free
或realloc
函数的参数必须是最开始calloc
函数分配的内存区域的原始首地址。
3、realloc
函数
realloc —— re - allocation 重分配,再分配
3.1 函数原型
void * realloc(void * p,size_t new_size);
3.2 功能
- 使用前必须存在已经被临时分配的内存区域(
malloc
、calloc
或realloc
),再将临时分配的这块内存进行重新分配,内存总长度更改为 new_size 个字节。 - 返回void型指针,指向重分配的内存的首地址,重分配之后内存区域可能会改变,函数可能将内存块移动到其他地方,因此返回的地址与传入的地址不一定相同。因此,一旦 realloc 函数返回,必须将指向原内存块的所有指针进行更新。
3.3 调用方法
int* p = (int*)malloc(sizeof(int));
int n = 2;
int* new = (int*)realloc(p, n*sizeof(int));
调用举例:
#include <stdio.h> /* printf, scanf, puts */
#include <stdlib.h> /* realloc, free, exit, NULL */
int main ()
{
int input,n;
int count = 0;
int* numbers = NULL;
int* more_numbers = NULL;
do {
printf ("Enter an integer value (0 to end): ");
scanf ("%d", &input);
count++;
more_numbers = (int*) realloc (numbers, count * sizeof(int));
if (more_numbers!=NULL) {
numbers=more_numbers;
numbers[count-1]=input;
}
else {
free (numbers);
puts ("Error (re)allocating memory");
exit (1);
}
} while (input!=0);
printf ("Numbers entered: ");
for (n=0;n<count;n++) printf ("%d ",numbers[n]);
free (numbers);
return 0;
}
3.4 注意事项
- 调用前需包含函数所在头文件,
#include <stdlib.h>
- 因为是临时分配的内存空间,使用完之后需要释放
free(p);
free
函数的参数必须是最开始realloc
函数分配的内存区域的原始首地址。
4、释放内存空间
4.1 free
函数
动态分配的内存块都来自于“堆”, 分配的区域使用结束之后,必须手动释放原分配的内存空间,否则会丢失内存块的记录,且浪费空间。
p = malloc(...);
q = malloc(...);
p = q; //错误做法,p原来指向的内存区域丢失,无法访问,也无法再次被分配,成为垃圾区域
p = malloc(...);
q = malloc(...);
free(p);
p = q; //正确做法
free
函数的参数必须为原来动态分配的内存地址,或空指针(无效调用,但无害)。否则将导致未定义行为。
4.2 悬空指针
某指针指向的内存区域被释放之后,该指针成为“悬空指针”,若此时试图修改该指针指向的内存区域将导致未定义行为,属于严重的错误。
char* p = (char*)malloc(4);
...
free(p);
...
strcpy(p, "abc"); //严重错误