系列文章目录

个人主页:古德猫宁-

信念如阳光,照亮前行的每一步


文章目录

  • 系列文章目录
    • *信念如阳光,照亮前行的每一步*
  • 前言
  • 一、对NULL指针的解引用操作
  • 二、对动态开辟空间的越界访问
  • 三、对非动态开辟内存使用free释放
  • 四、使用free释放一块动态开辟内存的一部分
  • 五、 对同一块动态内存多次释放
  • 六、动态开辟内存忘记释放(内存泄漏)
  • 总结

前言

本节目标:前面的文章介绍了C语言的动态内存开辟,本文重点讲述常见的动态内存的错误


一、对NULL指针的解引用操作

void test(){int* p = (int*)malloc(sizeof(int));*p = 20;free(p);}

解释:

这段代码的主要目的是使用动态内存分配(malloc)为一个整数分配内存,将其值设置为 20,然后使用 free 函数释放该内存。从代码的角度来看,没有显著的问题,但是有一些潜在的注意事项:

在实际的程序中,最好在调用 malloc 后检查分配是否成功。如果内存分配失败,malloc 将返回 NULL,释放内存后,我们最好将指针置为 NULL,以避免悬挂指针问题

修改后:

void test(){int* p = (int*)malloc(10*sizeof(int));*p = 20;//如果p的值是NULL,就会有问题if (p == NULL)//判断分配是否成功{perror("malloc");//报错return 1;}free(p);//释放空间p = NULL;}

二、对动态开辟空间的越界访问

int main(){int* p = (int*)malloc(40);if (p == NULL){return 1;}//使用int i = 0;for (i = 0; i <= 10; i++){*(p + i) = i;//当循环到第11次时就越界访问了}//free(p);p = NULL;return 0;}


解释:

上面这段代码中使用 malloc 分配了 40 字节的内存,然后在循环中尝试写入 11 个整数。由于数组索引是从 0 开始的,因此当 i 等于 10 时,实际上是在访问 *(p + 10),这越过了为数组分配的内存范围。

所以我们可以这样改:

#includeint main() {int* p = (int*)malloc(11*sizeof(int));//分配足够的空间来存储11个整数if (p == NULL){return 1;}//使用int i = 0;for (i = 0; i <=11; i++){*(p + i) = i;//循环的上限改为 10,不会越界访问}//free(p);p = NULL;return 0;}

三、对非动态开辟内存使用free释放

int main(){int a = 10;int* p = (int*)malloc(40);if (p == NULL){return 1;}//使用//...p = &a;//p指向的空间就不再是堆区上的空间free(p);p = NULL;//....return 0;}


解释:

  • 这段代码存在一个问题,即在将指针 p 指向堆分配的内存后,又将其指向了一个栈上的变量 a,然后尝试使用 free(p)释放内存。这是不正确的,因为 p 指向的不再是通过 malloc 分配的堆内存,而是指向了栈上的变量。
  • 当我们使用 free 函数释放内存时,应该确保指针指向的是通过 malloc 或类似函数动态分配的内存块。在这种情况下,并没有释放通过 malloc 分配的内存,而是尝试释放栈上的变量,这可能导致未定义的行为。
  • 所以在释放通过 malloc 分配的内存,我们确保在释放之前不要改变指针 p 指向的位置。

修改后:

#include int main() {int a = 10;int* p = (int*)malloc(40);if (p == NULL) {return 1;}// 使用//...// 不要改变指针 p 指向的位置,不要执行 p = &a;// 释放内存free(p);p = NULL;//....return 0;}

四、使用free释放一块动态开辟内存的一部分

#includeint main(){int a = 10;int* p = (int*)malloc(40);if (p == NULL){return 1;}//使用p++;//释放free(p);p = NULL;return 0;}

解释:

  • 这段代码存在一个问题。在使用 malloc 分配内存后,我们将指针 p 递增了一个位置 (p++),然后尝试使用 free 函数释放内存。这样做是不正确的,因为 p 不再指向 malloc 分配的起始位置,而是指向了分配内存块的下一个位置。free 函数要求传入的指针必须指向通过 malloc 等分配函数返回的精确地址。
  • 如果对指针进行了移动,应该确保在释放内存之前将指针重新指向 malloc 分配的起始位置。

修改后:

#include int main() {int a = 10;int* p = (int*)malloc(40);if (p == NULL) {return 1;}// 使用p++; // 移动指针// 重新指向起始位置p--;// 释放内存free(p);p = NULL;return 0;}

五、 对同一块动态内存多次释放

int main(){int a = 10;int* p = (int*)malloc(40);if (p == NULL){return 1;}//使用//释放free(p);p = NULL;free(p);p = NULL;return 0;}


解释:

  • 上面这段代码存在一个问题,即在释放内存后又尝试再次释放相同的内存块。这是不正确的,因为一旦通过 free 函数释放了内存,就不应该再次使用相同的指针来释放。这可能导致未定义的行为,甚至程序崩溃。
  • 代码中,第一次调用 free(p) 将内存释放了,并将指针 p 设置为 NULL。然后,你又尝试使用 free(p),这是不安全的。

修正这个问题的方法是只调用一次 free,而不是两次:

#include int main() {int a = 10;int* p = (int*)malloc(40);if (p == NULL) {return 1;}// 使用// 释放内存free(p);p = NULL;// 避免再次释放相同的内存块// free(p); // 这一行应该注释掉或删除return 0;}

六、动态开辟内存忘记释放(内存泄漏)

void test(){int* p = (int*)malloc(100);if (NULL != p){*p = 20;}free(p);p = NULL;}int main(){test();//while (1);}


解释:

  • 上面这段代码中,在结尾没有调用 free 来释放动态内存分配的空间,而且main函数无限循环,这个函数被调用多次,每次都分配内存但没有释放,程序将持续占用更多的内存,最终可能导致系统资源(内存池)不足,这类问题称为内存泄漏。

修改后:

void test(){int* p = (int*)malloc(100);if (NULL != p){*p = 20;}free(p);p = NULL;}int main(){test();return 0; // 添加这行以正常退出程序}

忘记释放不再使用的动态开辟的空间会造成内存泄漏
切记:动态开辟的空间一定要释放,并且正确释放。


总结

各位要对上面常见的动态内存开辟有一个清晰的认知,并避免这些问题的出现,今天的笔记到此结束啦