前言:之前学习了数组,数组的元素储存在内存中连续位置。在声明数组时必须要指定数组的元素个数,即数组空间大小在声明时已经确定了。但是需存放的元素个数常常在运行时才能知道(取决于输入的数据)。这会有几个缺点:1. 当输入元素个数大于数组声明的元素个数时会带来意想不到错误 2. 当输入元素个数小于数组声明的元素个数时会带来内存空间的浪费 3. 数组大小不能动态调整。
C语言提供了相关的动态内存分配函数,需要多大内存空间就分配多大内存空间,并且可以动态调整已分配的内存空间大小
一、动态内存分配函数 使用下面相关动态内存函数需要引用头文件 <stdlib.h>
1.1 malloc和free函数
malloc函数功能: 向内存申请指定大小的连续内存空间,申请成功返回该空间起始地址,申请失败返回NULL指针 库函数malloc声明void* malloc (size_t size); 返回值:申请成功返回该空间起始地址,申请失败返回NULL指针,因为不知道申请的空间要存放什么类型数据所以返回void*类型 size: 申请分配的内存大小,单位为字节
注意:
malloc返回值有可能是NULL指针,使用前需要检查
malloc申请的空间并没有被初始化
free函数功能: 释放申请的动态内存分配的空间(即malloc、calloc、realloc函数申请的空间)
库函数free声明void free (void* ptr); ptr : 指向先前用malloc、calloc或realloc分配的内存块的指针
注意:1.如果 ptr 指向的空间不是动态开辟的,那free函数的行为是未定义的 2. 如果ptr 是NULL指针,则函数什么事都不做 3. 只会释放ptr指向空间的值,但ptr本身不会被置空
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 #include <stdio.h> #include <stdlib.h> int main () { int * p = (int *)malloc (10 * sizeof (int )); if (p == NULL ) { return -1 ; } int i = 0 ; for (i = 0 ; i < 10 ; i++) { printf ("%d " , *(p + i)); } printf ("\n" ); for (i = 0 ; i < 10 ; i++) { *(p + i) = i; } for (i = 0 ; i < 10 ; i++) { printf ("%d " , *(p + i)); } printf ("\n" ); free (p); p = NULL ; return 0 ; }
输出
1 2 -842150451 -842150451 -842150451 -842150451 -842150451 -842150451 -842150451 -842150451 -842150451 -842150451 0 1 2 3 4 5 6 7 8 9
1.2 calloc函数 calloc函数功能: calloc函数与malloc函数功能一样,区别主要在于calloc会对分配的空间初始化为0,另外它们请求内存大小的方式不同
库函数calloc声明void* calloc (size_t num, size_t size); 返回值:申请成功返回该空间起始地址,申请失败返回NULL指针,因为不知道申请的空间要存放什么类型数据所以返回void*类型 num:元素个数 size: 元素大小申请内存空间大小=num*size
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 #include <stdio.h> #include <stdlib.h> int main () { int * p = (int *)calloc (10 ,sizeof (int )); if (p == NULL ) { return -1 ; } int i = 0 ; for (i = 0 ; i < 10 ; i++) { printf ("%d " , *(p + i)); } printf ("\n" ); for (i = 0 ; i < 10 ; i++) { *(p + i) = i; } for (i = 0 ; i < 10 ; i++) { printf ("%d " , *(p + i)); } printf ("\n" ); free (p); p = NULL ; return 0 ; }
输出
1 2 0 0 0 0 0 0 0 0 0 0 0 1 2 3 4 5 6 7 8 9
使用malloc还是calloc函数取决是否要对动态内存分配的空间初始化
1.3 realloc函数 realloc函数功能: realloc对动态内存空间大小进行扩大或缩小
库函数realloc声明void* realloc (void* ptr, size_t size); 返回值:返回调整后空间的起始地址,调整失败返回NULL指针 ptr:指向先前用malloc、calloc或realloc分配的内存块的指针 size:动态内存空间新大小,单位为字节
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 #include <stdio.h> #include <stdlib.h> int main () { int * p = (int *)malloc (10 * sizeof (int )); if (p == NULL ) { return -1 ; } int i = 0 ; for (i = 0 ; i < 10 ; i++) { *(p + i) = i; } int * ptr = (int *)realloc (p, 20 * sizeof (int )); if (ptr == NULL ) { printf ("空间调整失败\n" ); } else { p = ptr; ptr = NULL ; for (i = 10 ; i < 20 ; i++) { *(p + i) = i; } for (i = 0 ; i < 20 ; i++) { printf ("%d " , *(p + i)); } } free (p); p = NULL ; return 0 ; }
输出
1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
二、常见的动态内存错误 2.1 对NULL指针进行解引用操作 1 2 3 4 5 6 7 8 9 #include <stdio.h> #include <stdlib.h> int main () { int * p = (int *)malloc (100000000000000000 * sizeof (int )); *p = 10 ; return 0 ; }
申请动态内存分配失败,p为NULL指针,对p解引用修改存储的值会错误
2.2 动态内存分配空间的越界访问 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #include <stdio.h> #include <stdlib.h> int main () { int * p = (int *)malloc (10 * sizeof (int )); if (p == NULL ) { return -1 ; } int i = 0 ; for (i = 0 ; i <= 10 ; i++) { *(p+i) = i; } free (p); p = NULL ; return 0 ; }
当i=10,*(p+i)越界访问了
2.3 对非动态内存分配的空间free释放 1 2 3 4 5 6 7 8 9 10 11 #include <stdio.h> #include <stdlib.h> int main () { int i = 10 ; int * p = &i; free (p); p = NULL ; return 0 ; }
p指向空间不是动态内存分配的空间
2.4 对动态内存分配的空间的一部分free释放 1 2 3 4 5 6 7 8 9 10 11 12 13 14 #include <stdio.h> #include <stdlib.h> int main () { int * p = (int *)malloc (10 * sizeof (int )); if (p == NULL ) { return -1 ; } free (p+5 ); p = NULL ; return 0 ; }
p+5指向空间是动态内存分配的空间的一部分
2.5 对已经free的动态内存分配空间访问或再次free 1 2 3 4 5 6 7 8 9 10 11 12 #include <stdio.h> #include <stdlib.h> int main () { int * p = (int *)malloc (10 * sizeof (int )); free (p); *p = 10 ; free (p); p = NULL ; return 0 ; }
2.6 内存泄漏 当申请的动态内存空间不需要时应该被释放,这样可以重新分配使用。申请的空间在使用完毕后不free释放将引起内存泄漏。内存泄漏将一点点榨干可用内存,最终导致系统崩溃
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #include <stdio.h> #include <stdlib.h> void test () { int * p = (int *)malloc (10 *sizeof (int )); } int main () { while (1 ) { test(); } return 0 ; }
当业务处理没有满足条件则while为死循环,每次循环都会申请内存空间,最终将系统崩溃