前言:
在上章节中讲解了动态内存的概念和管理的核心函数。
在本章节继续为大家介绍动态内存的常见错误,让大家更好的理解运用。
补充:使用内存函数需要头文件<stdlib.h>
对NULL指针的解引用操作
- 当使用
malloc
、calloc
或realloc
等函数分配内存时,如果返回值为NULL(表示分配失败),直接对NULL指针进行解引用会导致程序崩溃。例如:
int *p = (int*)malloc(10 * sizeof(int));if (p == NULL)
{// 处理分配失败的情况
}
正确的做法是先检查返回值是否为NULL,避免直接使用指针。
越界访问
在访问动态分配的内存时,如果超出分配的范围,可能会导致程序崩溃或未定义行为。
例如:
int *arr = (int*)malloc(5 * sizeof(int));for (int i = 0; i <= 5; i++)
{arr[i] = i; // 越界访问
}free(arr);
解决方法是确保访问的索引在有效范围内。
释放非动态分配的内存
使用free
函数释放非动态分配的内存(如栈上分配的内存)会导致未定义行为。例如:
int arr[10];free(arr); // 错误:释放栈上分配的内存
应确保只对动态分配的内存调用free
。
重复释放内存(双重释放)
如果对同一块内存多次调用free
函数,会导致程序崩溃或未定义行为。例如:
int *ptr = (int*)malloc(10 * sizeof(int));free(ptr);free(ptr); // 错误:双重释放
释放部分内存
如果尝试释放动态分配内存的一部分,会导致未定义行为。例如:
int *ptr = (int*)malloc(10 * sizeof(int));free(ptr + 2); // 错误:释放部分内存
正确的做法是释放整个内存块。
int main()
{int* p = (int*)malloc(40);if (p == NULL){perror("malloc");return 0;}int i = 0;for (i = 0;i < 10;i++){*p = 5;p++;}free(p);p = NULL;return 0;
}
未初始化的指针使用
使用未初始化的指针可能导致程序崩溃或访问非法内存。例如:
int *ptr;printf("%d\n", *ptr); // 错误:未初始化的指针
指针在使用前应确保已正确初始化。
内存泄漏
如果分配的内存未被释放,会导致内存泄漏。
例如:
int *ptr = (int*)malloc(10 * sizeof(int));// 程序结束时未释放内存
void test()
{int* p = (int*)malloc(100);if (*p = NULL){*p = 20;}
}
int main()
{test();while (1);return 0;
}
野指针的使用
已释放的指针或指向不存在内存的指针被称为野指针,使用野指针可能导致程序崩溃。例如:
int *ptr = (int*)malloc(10 * sizeof(int));free(ptr);printf("%d\n", *ptr); // 错误:野指针
缓冲区溢出
在访问动态分配的数组时,如果超出数组边界,可能导致缓冲区溢出。例如:
int *arr = (int*)malloc(5 * sizeof(int));for (int i = 0; i < 6; i++) {arr[i] = i; // 缓冲区溢出}free(arr);
未检查realloc
失败
使用realloc
调整内存大小时,如果失败会返回NULL,但原内存块可能仍然有效。例如:
int *ptr = (int*)malloc(10 * sizeof(int));ptr = (int*)realloc(ptr, 20 * sizeof(int));if (ptr == NULL) {// 处理失败情况}free(ptr);
总结:
C语言中的动态内存管理涉及复杂的操作,容易引发多种错误。开发者应严格遵循以下原则:
- 在使用前检查指针是否为NULL。
- 确保访问内存时在有效范围内。
- 避免重复释放内存。
- 及时释放不再使用的动态内存。
- 使用工具(如Valgrind)检测潜在问题。