最后
不知道你们用的什么环境,我一般都是用的Python3.6环境和pycharm解释器,没有软件,或者没有资料,没人解答问题,都可以免费领取(包括今天的代码),过几天我还会做个视频教程出来,有需要也可以领取~
给大家准备的学习资料包括但不限于:
Python 环境、pycharm编辑器/永久激活/翻译插件
python 零基础视频教程
Python 界面开发实战教程
Python 爬虫实战教程
Python 数据分析实战教程
python 游戏开发实战教程
Python 电子书100本
Python 学习路线规划
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
一、内存划分
要理解动态内存管理,首先要了解C程序对内存划分的主要形式:
栈区 ①在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。 ②栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。(栈溢出问题) ③ 栈区主要存放运行函数而分配的局部变量、函数参数、返回数据、返回地址等。 ④向下增长指从栈依次申请的地址在减小 ⑤栈可以通过函数_alloca进行动态分配,不过注意,所分配空间不能通过free或delete进行释放 堆区 ①一般由程序员释放, 若程序员不释放,程序结束时可能由OS(operate system)回收 。分配方式类似于链表。 ②动态内存开辟在堆区上。 ③向上增长指从堆依次申请的地址在增加 ④堆无法静态分配,只能动态分配 数据段(静态区) ①存放全局变量、静态数据。 ②程序结束后由系统释放。 代码段 ①存放函数体(类成员函数和全局函数)的二进制代码。 ②其中的数据只可被读取,不可被修改
(上图来源比特科技
二、四大函数
①malloc
作用:在堆区申请一块size_t大小(单位字节)的空间。成功返回动态开辟空间的地址,失败返回空指针NULL 。(比如开辟的空间太大了就会失败)
注意点:1.size大小为0的情况是未定义的,其结果取决于编译器的处理
2.由于malloc返回值为void*类型,在用指针接收时最好先强制类型转换
3.小心内存开辟失败返回空指针
#include<stdlib.h>
int main()
{
int*p = (int *)malloc(40);
return 0;
}
看!我们很轻松的动态开辟了一块指定大小的空间。但不知道你是不是有这样的疑惑:一直开辟空间那电脑的内存不会越来越小吗? 其实当我们程序结束时,动态开辟的空间会被自动回收,当然我们也可以选择主动出击——使用free函数。
②free
作用:释放动态开辟的空间(memblock为指向动态开辟空间的指针)
注意点:1.free(NULL),函数不执行任何操作
2.不能用free函数释放非动态开辟的空间
3.free只是释放空间,并没有清除指针,也就是说你和女朋友分手了,但你仍然牢 牢记着人家的电话号码(空间地址)。若此时对指针解引用,就犯了非法访问内存 的错误,所以要及时将指针赋值为空,对人家彻底死心。
#include<stdlib.h>
#include<assert.h>
int main()
{
int i = 0;
int*p = (int *)malloc(10*sizeof(int));
assert(p);//监测是否开辟成功
for (i = 0; i < 10; i++)
{
p[i] = i;//或者为*(p+i),但不可以是p++。
}
for (i = 0; i < 10; i++)
{
printf("%d ",p[i]);
}
free(p);
p = NULL;//最好置为NULL
return 0;
}
malloc和free的组合拳,完美的完成了动态内存开辟的过程。但其实动态开辟内存不只是malloc的特权,calloc也具有,只不过二者的作用有所不同
③calloc
作用:向堆区申请一块空间,存放num个size大小的元素。成功返回指向开辟空间的指针,失败则返回NULL。
区别:calloc开辟空间后会将每个元素初始化为0。所以malloc开辟空间效率更高,calloc会自动赋值为0,各有所长。
calloc开辟——初始化为0
malloc开辟——未初始化
认识了malloc,calloc这类动态开辟空间的函数,聪明的你或许会想到如果开辟的空间不够怎么办。别担心,C语言给出了realloc函数调整动态开辟空间的大小。
④realloc
作用:将动态开辟的内存大小调整为size(单位字节)
注意要深刻理解realloc调整大小的两种情况:
情况一:
若原有空间充足,则就直接在原有内存之后直接追加空间,原来空间的数据不发生变化。
情况二:
原有空间之后没有足够多的空间时,扩展的方法是:在堆空间上另找一个合适大小的连续空间来使用,复制原来的内容再追加。这样函数返回的是一个新的内存地址。
malloc创建的p所指向的空间
realloc后p1指向的空间
realloc之后p指向的空间
我们根据上面的分析可以得出以下结论(空间不足时):
1.返回一个新的地址指向新开辟的空间
2.原空间的内容会被复制到新开辟的空间
3.原空间的内容被释放
当传入的指针为空指针时,此时realloc的功能相当于malloc的。
三、易错分析
试着分析一下以下代码有什么缺陷或者不足
问题一:
#include<stdlib.h>
#include<assert.h>
//缺陷版
int main()
{
int i = 0;
int*p = (int *)malloc(5*sizeof(int));
for (i = 0; i < 5; i++)
{
p[i] = i;
}
p = realloc(p, 1000000);//ok?
free(p);
p = NULL;
}
//完善版
int main()
{
int i = 0;
int*p = (int *)malloc(5*sizeof(int));
assert(p);
for (i = 0; i < 5; i++)
{
p[i] = i;
}
int *p1 = (int *)realloc(p, 1000);
if(p1!=NULL)
{
p=p1;//为了程序的连贯性,最好都赋值给最开始使用的p
}
free(p);
p = NULL;
}
1.无论是malloc或者是realloc都要小心内存开辟失败返回NULL的情况,所以检验必不可少
2.“p = realloc(p, 1000000);”的写法如果失败那原来的地址也找不到了,可谓是赔了夫人又折兵。
问题二:
void test()
{
int i = 0;
int *p = (int *)malloc(10 * sizeof(int));
if (NULL == p)
{
exit(EXIT_FAILURE);
}
for (i = 0; i <= 10; i++)//ok?
{
*(p + i) = i;
}
free(p);
}
1.注意越界访问的问题,"i<10"才不会让程序崩溃
问题三:
void test()
{
int a = 10;
int *p = &a;
free(p);//ok?
}
1.不能用free释放不是动态开辟的内存
问题四:
void test()
{
int *p = (int *)malloc(100);
p++;//ok?
free(p);
}
1.p的地址不能改变,否则在free时会产生释放部分空间错误
问题五:
void test()
{
int *p = (int *)malloc(100);
free(p);
free(p);
}
1.很显然该程序犯了重复释放的错误
2.在这段简短的代码中可以轻而易举的发现问题,但如果在很长的代码中我们如何避免?
①做到谁开辟谁释放空间
②养成将指针置为NULL的习惯,这样即使被free也是free(NULL),如前文所说无事发生
问题六:
void test()
{
int *p = (int *)malloc(100);
if (NULL != p)
{
*p = 20;
}
}
int main()
{
test();
while (1);
}
1.执行上述程序时,打开任务管理器可以发现CPU被占用的空间一直增大(电脑有保护机制,到一定程度就停止增长),一直开辟空间但是不释放使我们空余的内存越来越少,这也是就我们所说的内存泄漏问题
2.所以动态开辟的内存一定要释放并且正确释放。
四、经典面试题
面试题一:
void GetMemory(char *p)
{
p = (char *)malloc(100);
}
int main()
{
char *str = NULL;
GetMemory(str);
strcpy(str, "hello world");
printf(str);
return 0;
}
请问运行的结果是什么? 程序崩溃
分析:初学者往往很容易进入这个误区,认为str成功指向了开辟的空间。可仔细一想,str真的如我们所想吗?在这里我们混淆了传址操作和传参操作,图中的操作属于传参操作,p不过是str的一份临时拷贝,改变p对str没有一点影响。当然上面的代码也存在没有检查返回值,没有主动释放内存的错误。
我们如何纠正呢,在接下来的面试题里做详细解答。
面试题二:
char *GetMemory(void)
{
char p[] = "hello world";
return p;
}
int main()
{
char *str = NULL;
str = (char*)GetMemory();
printf(str);
return 0;
现在能在网上找到很多很多的学习资源,有免费的也有收费的,当我拿到1套比较全的学习资源之前,我并没着急去看第1节,我而是去审视这套资源是否值得学习,有时候也会去问一些学长的意见,如果可以之后,我会对这套学习资源做1个学习计划,我的学习计划主要包括规划图和学习进度表。
分享给大家这份我薅到的免费视频资料,质量还不错,大家可以跟着学习
![](https://img-blog.csdnimg.cn/img_convert/21b2604bd33c4b6713f686ddd3fe5aff.png)
**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
**[需要这份系统化学习资料的朋友,可以戳这里获取](https://bbs.csdn.net/topics/618317507)**
**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**