C语言【动态内存管理 后篇】
创始人
2024-05-26 07:40:10
0

动态内存管理 后篇

  • 🫅经典例题
    • 🤦‍♂️题目1
    • 🤦‍♂️题目2
    • 🤦‍♂️题目3
    • 🤦‍♂️题目4
  • 🫅C/C++程序的内存开辟

前面的一篇文章动态内存管理 前篇,我们已经了解过了动态内存管理的相关信息,接下来,让我们来一起来踩“坑”。当然,今天的踩坑,是为了面试不踩坑哦

🫅经典例题

🤦‍♂️题目1

void GetMemory(char* p)
{p = (char*)malloc(100);
}void Test(void)
{char* str = NULL;GetMemory(str);strcpy(str, "hello world");printf(str);
}int main()
{Test();return 0;
}

我们来找一找上面的代码有什么问题?
好了,不绕弯子了,来看看到底错了哪些地方

  1. GetMemory(str)中是传值,而不是传址。当出了GetMemory函数,p指向的地址发生了变化,但str依旧是NULL,然后就进入了strcpy函数,这样就形成了非法访问内存(NULL是操作系统的,我们用户没有权限访问)
  2. 在GetMemory函数内部,动态申请了内存,直到程序结束也没有释放内存,这样会导致内存泄漏

🐉🐉🐉🐉🐉
printf(str)???这样写也能行???为什么吗??
我们来看看下面的代码:

#includeint main()
{char ptr[] = "hehehe";char* p = "hehehe";printf("%s\n", ptr);printf("%s\n", p);printf("hehehe");return 0;
}//运行结果:
*****
hehehe
hehehe
hehehe
*****

纳尼??这是为什么呢?
在解释这个问题前,我们先想一下printf的第一个参数是什么?答案是:必须是指针。那就意味printf(“hehehe”)里面的“hehehe”不是我们所看到的那样,而是一个地址。那么它是谁的地址?下面的代码会给我们答案:

#includeint main()
{char* p = "hehehe";printf("%p\n", "hehehe");printf("%p\n", p);return 0;
}//运行结果:
*****
00AF7B54
00AF7B54*****

奥,那我就明白了:ptr指针指向的是字符串的第一个字符,那么运行结果告诉我们,printf(“hehehe”)里面传的就是第一个字符的地址。

修改:

//修改后
// #include
#includevoid GetMemory(char** p)  //二级指针接收
{*p = (char*)malloc(100);  //解引用就是一级指针
}void Test(void)
{char* str = NULL;GetMemory(&str);   //传址strcpy(str, "hello world");printf(str);//释放空间free(str);str = NULL;}int main()
{Test();return 0;
}

🤦‍♂️题目2

#include
#includechar* GetMemory(void)
{char p[] = "hello world";return p;
}void Test(void)
{char* str = NULL;str = GetMemory();printf(str);
}int main()
{Test();return 0;
}

这个除了没有释放空间就没有其他的问题了吧。
但真相是这样吗??
让我们看看运行结果是什么吧

嗯??没释放空间也不会导致这种结果吧…

🐉🐉🐉🐉🐉
让我们来全面的分析一下吧:

  1. 关于函数返回信息的问题,这一类问题也叫作“返回栈空间地址的问题”:函数返回地址,地址信息存在,但函数内部的栈空间就还给操作系统了,操作系统可以把此空间作为别的变量的地址
  2. 函数动态申请空间,直到程序结束也没有释放掉空间,会导致内存泄漏

返回栈空间地址的问题???这是什么啊??

栈区主要存放运行函数而分配的局部变量、函数参数、返回数据、返
回地址等

那我们就明白了为什么叫做返回栈空间地址,那问题呢??
当函数调用结束(出了函数),在函数里面的空间就会返还给操作系统,我们用户没有操作权限。操作系统会把这个空间重新分配给其他变量,这样我们就不得而知了

修改:

#include
#includechar* GetMemory(void)
{char* p = "hello world";  //将第一个字符的地址传给p//static char p[]="hello world";    //被static修饰的变量存放在数据段(静态区),//数据段的特点是在上面创建的变量,直到程序结束才销毁return p;
}void Test(void)
{char* str = NULL;str = GetMemory();printf(str);//释放free(str);str = NULL;
}int main()
{Test();return 0;
}

🤦‍♂️题目3

#include
#includevoid GetMemory(char** p, int num)
{*p = (char*)malloc(num);
}void Test(void)
{char* str = NULL;GetMemory(&str, 100);strcpy(str, "hello");printf(str);
}int main()
{Test();return 0;
}

这个就很简单了,没有释放空间

修改:

#include
#includevoid GetMemory(char** p, int num)
{*p = (char*)malloc(num);
}void Test(void)
{char* str = NULL;GetMemory(&str, 100);strcpy(str, "hello");printf(str);//释放free(str);str = NULL;
}int main()
{Test();return 0;
}

🤦‍♂️题目4

#include
#includevoid Test(void)
{char* str = (char*)malloc(100);strcpy(str, "hello");free(str);if (str != NULL){strcpy(str, "world");printf(str);}
}int main()
{Test();return 0;
}

这个问题是啥?感觉没问题啊??不会是str没有置空吧?
哈哈,其实这个问题很隐蔽,不注意看是看不出来的

free的动作是将该内存块返还给操作系统,我们没有操作权限,故str是野指针,造成了非法访问内存

修改:

#include
#includevoid Test(void)
{char* str = (char*)malloc(100);strcpy(str, "hello");free(str);str = NULL;  //在这里置空,也是为了符合下面判断的逻辑//那么也不会进入if里面造成非法访问if (str != NULL){strcpy(str, "world"); printf(str);}
}int main()
{Test();return 0;
}

🫅C/C++程序的内存开辟

在在这里,终于把C语言中的动态内存管理的知识讲解完了。想必大家大概的框架是有的,但是对一些专业术语跟底层逻辑不是很清楚。比如:什么是堆区,什么又是栈区?内存中的每一个区域的作用是什么?等等

在这里插入图片描述
C/C++程序内存分配的几个区域:

  1. 栈区(stack):在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结
    束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是
    分配的内存容量有限。 栈区主要存放运行函数而分配的局部变量、函数参数、返回数据、返
    回地址等。
  2. 堆区(heap):一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。分
    配方式类似于链表。
  3. 数据段(静态区)(static)存放全局变量、静态数据。程序结束后由系统释放。
  4. 代码段:存放函数体(类成员函数和全局函数)的二进制代码。字符串常量就在其中
  • 实际上普通的局部变量是在栈区分配空间的,栈区的特点是在上面创建的变量出了作用域就销毁。
    但是被static修饰的变量存放在数据段(静态区),数据段的特点是在上面创建的变量,直到程序
    结束才销毁。所以生命周期变长。

在这里插入图片描述
码文不易,各位看官一键三连哦 💕💕💕
各位的鼓励与支持是我前进最大的动力

相关内容

热门资讯

监控摄像头接入GB28181平... 流程简介将监控摄像头的视频在网站和APP中直播,要解决的几个问题是:1&...
Windows10添加群晖磁盘... 在使用群晖NAS时,我们需要通过本地映射的方式把NAS映射成本地的一块磁盘使用。 通过...
protocol buffer... 目录 目录 什么是protocol buffer 1.protobuf 1.1安装  1.2使用...
在Word、WPS中插入AxM... 引言 我最近需要写一些文章,在排版时发现AxMath插入的公式竟然会导致行间距异常&#...
【PdgCntEditor】解... 一、问题背景 大部分的图书对应的PDF,目录中的页码并非PDF中直接索引的页码...
修复 爱普生 EPSON L4... L4151 L4153 L4156 L4158 L4163 L4165 L4166 L4168 L4...
Fluent中创建监测点 1 概述某些仿真问题,需要创建监测点,用于获取空间定点的数据࿰...
educoder数据结构与算法...                                                   ...
MySQL下载和安装(Wind... 前言:刚换了一台电脑,里面所有东西都需要重新配置,习惯了所...
MFC文件操作  MFC提供了一个文件操作的基类CFile,这个类提供了一个没有缓存的二进制格式的磁盘...