动态内存的意义
在我们空间的开辟有定义变量
#include<stdio.h> int main() { int a = 0; char arr[] = "sdsd"; printf("%p\n", &a); printf("%p\n", arr); return 0; }
甚至还有局部变量都会向内存申请空间,但是这些空间不擦长久,或者很难随意操作这些空间进行销毁,或者不能随意扩大空间
但是上述的开辟空间的方式有两个特点:
- 空间开辟大小是固定的。
- 数组在申明的时候,必须指定数组的长度,它所需要的内存在编译时分配。
动态内存函数的介绍
malloc
这个函数的动态内存开辟函数,
size_t是一个无符号整数类型,表示需要分配的内存大小(以字节为单位)。malloc函数会在堆上分配一块指定大小的内存,并返回指向该内存块的指针。如果分配失败,则返回NULL。
#include <stdio.h> #include <stdlib.h> #include <limits.h> int main() { int* arr = (int*)malloc(sizeof(int) * INT_MAX); if (arr == NULL) { perror("malloc:"); return 1; } return 0; }
可能有一些人会利用malloc申请0个字节,这种做法本身没有错误,但是这个就是脱裤子放屁多此一举
如果参数 size 为0,malloc的行为是标准是未定义的,取决于编译器。
free
有心细的小可爱就会发现,当我们程序运行结束就会释放出开辟的动态内存(被动),如果我们要自己释放就要用到free函数
free是C语言中用于释放动态分配内存的函数。当我们使用malloc、calloc等函数在程序运行期间动态申请内存时,需要在使用完毕后使用free函数将其释放回系统,以便其他程序或进程可以使用。
其中,ptr是指向需要释放的内存块的指针。使用free函数可以将之前通过malloc、calloc等函数动态分配的内存块释放回系统,以避免内存泄漏和浪费。
#include <stdio.h> #include <stdlib.h> #include <limits.h> int main() { int* arr = (int*)malloc(sizeof(int) * 10); if (arr == NULL) { perror("malloc"); return 1; } int i = 0; for (i = 0; i < 10; i++) { arr[i] = i; printf("%d ", arr[i]); } free(arr); arr = NULL; return 0; }
需要注意的是,在使用free函数释放内存块时需要注意以下几点:
只能释放之前通过malloc、calloc等函数动态分配的内存块,不能释放栈上的局部变量和全局变量等静态分配的内存;
不能重复释放同一块内存,否则会导致程序崩溃;
释放内存后,应该将指针设置为NULL,以避免出现野指针问题。
calloc
calloc是C语言中用于动态分配内存并进行初始化的函数。与malloc函数相似,calloc也用于在程序运行时动态分配内存,但与malloc不同的是,calloc会将分配的内存块初始化为零,而malloc不会。
其中,num表示需要分配的元素个数,size表示每个元素的大小。calloc函数会分配num个大小为size的连续内存块,并将其初始化为零。如果分配成功,则返回指向分配内存块的指针;如果分配失败,则返回NULL。
#include <stdio.h> #include <stdlib.h> #include <limits.h> int main() { int* arr = (int*)calloc(10, sizeof(int)); if (arr == NULL) { perror("calloc"); return 1; } int i = 0; for (i = 0; i < 10; i++) { arr[i] = i; } free(arr); arr = NULL; return 0; }
realloc
realloc函数的出现让动态内存管理更加灵活,前面的malloc和calloc申请的空间无法避免申请空间过大或者过小,realloc函数就可以做到动态开辟内存的大小进行调整
realloc是C语言中用于重新分配内存的函数。在程序运行期间,我们有时需要重新分配内存块的大小,可以使用realloc函数来实现。realloc函数可以将之前分配的内存块的大小调整为新的大小,并返回指向新内存块的指针。
ptr是指向之前分配的内存块的指针,size是需要重新分配的内存块的大小。
如果重新分配成功,则返回指向新内存块的指针;如果分配失败,则返回NULL。需要注意的是,如果新的大小小于原来的大小,则realloc函数会截断原来的内存块;如果新的大小大于原来的大小,则realloc函数会在原来的内存块后面分配新的内存块。
#include <stdio.h> #include <stdlib.h> #include <limits.h> int main() { int* arr = (int*)malloc(sizeof(int) * 10); int* arr1 = (int*)calloc(20, sizeof(int)); if (arr == NULL) { perror("malloc"); } if (arr1 == NULL) { perror("calloc"); } //调整空间 int *arr3 = (int*)realloc(arr, 2000 * sizeof(int)); if (arr3 != NULL) { arr = arr3; } free(arr); free(arr1); arr = NULL; arr1 = NULL; arr3 = NULL; return 0; }
realloc函数开辟的空间情况分两种:
第一种:
之前malloc开辟空间的后面刚好满足扩大后的空间大小就会在原来的地址处重新扩大,这种情况返回的新地址就会和旧地址的值是一样的
第二种:
之前malloc开辟的空间的后面不能满足扩大后的空间大小,就会放弃在原来的空间扩大,反而会重新找一块能满足条件的内存进行开辟,一次性开辟好,会将旧的空间的数据就会拷贝到新的空间,旧的空间就会销毁
常见的动态内存错误
对NULL指针的解引用操作
这种情况的出现是因为没有对返回值进行判断造成的
#include <stdio.h> #include <stdlib.h> #include <limits.h> int main() { int* arr = malloc(sizeof(int) * INT_MAX); int i = 0; for (i = 0; i < INT_MAX; i++) { arr[i] = 1; } return 0; }
如上面代码一样,没有判断是否创建成功,直接解引用操作就会报错
对动态内存的越界访问
#include <stdio.h> #include <stdlib.h> #include <limits.h> int main() { int* arr = (int*)calloc(10, sizeof(int)); if (arr == NULL) { perror("calloc"); return 1; } int i = 0; for (i = 0; i <= 10; i++) { arr[i] = i; } for (i = 0; i <= 10; i++) { printf("%d ", *(arr + i)); } free(arr); arr = NULL; return 0; }
这种情况就和我们创建数组,越界访问是一样的
对非动态开辟的内存进行free释放
#include <stdio.h> #include <stdlib.h> #include <limits.h> int main() { int a = 0; int* p = &a; free(p); p = NULL; return 0; }
这种情况就是懵了
使用free释放动态开辟的内存的一部分
#include <stdio.h> #include <stdlib.h> #include <limits.h> int main() { int* arr = calloc(10, sizeof(int)); int i = 0; for (i = 0; i < 5; i++) { *arr = i; arr++; } free(arr); arr = NULL; return 0; }
对一块空间多次释放
#include <stdio.h> #include <stdlib.h> #include <limits.h> int main() { int* arr = (int*)malloc(sizeof(int) * 10); if (arr == NULL) { perror("malloc"); return 1; } free(arr); free(arr); return 0; }
解决这个错误我们可以养成一个习惯,只要释放过的空间,对应的变量全部赋值为NULL,因为free释放NULL不会有啥错误
动态开辟内存忘记释放(内存泄漏)
include <stdio.h> #include <stdlib.h> #include <limits.h> void Other() { int* arr = (int*)malloc(sizeof(int) * 10); } int main() { Other(); while (1) { ; } return 0; }
这样很容易造成空间不足,我们要记住谁申请,谁释放,如果没有释放交接任务也要告诉别人释放
C语言进阶第九课 --------动态内存管理-2