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;
}
请问上述程序的结果是什么? 打印随机值。
分析:如果你认为GetMemory函数返回的是p的地址,没错,你想的很对。但此时的p所指向的内容不再是““hello world””,为什么呢?p[ ]数组在函数中创建,正如文章开头提到的内存布局,函数申请的局部变量开辟在栈区上,而开辟在栈区上变量的特点是当这个函数结束,变量也随即销毁,所以p只是记住当时的地址,里面的内容不再是"hello world"。如果此时对str进行解引用操作,则会出现非法访问内存的错误。
面试题三:
int main(void)
{
char *str = (char *)malloc(100);
strcpy(str, "hello");
free(str);
if (str != NULL)
{
strcpy(str, "world");
printf(str);
}
return 0;
}
请问上述程序的结果是什么? 打印“world”
分析:不知道有没有小伙伴们认为没有打印结果的,如果有,我猜想应该是对free的作用产生误解。free只是对堆区的内存进行释放,并不会将str指向的地址赋值为空指针。
如何修改面试一的程序:
分析完上面三个面试题后相信你对动态内存开辟有了更深的理解,我们再回到第一个问题,如何将他修改正确呢?其实面试题一中代码之所以失败的原因在于没有将p与str建立起练习,以此为突破口,我们可以有以下两个思路:1.传址操作 2.返回p的地址。
//采用传址操作
void GetMemory(char **p)
{
*p = (char *)malloc(100);
}
int main()
{
char *str = NULL;
GetMemory(&str);
if (str != NULL)
{
strcpy(str, "hello world");
printf(str);
}
free(str);
str = NULL;
return 0;
}
//返回p的地址
char* GetMemory(char *p)
{
p = (char *)malloc(100);
return p;
}
int main()
{
char *str = NULL;
str=(char *)GetMemory(str);
if (str != NULL)
{
strcpy(str, "hello world");
printf(str);
}
free(str);
str = NULL;
return 0;
}
五、柔性数组
1.前言
我们要向实现数组长度的动态变化,以下代码可以吗?
const int n = 0;
int main()
{
int arr[n] = { 0 };
return 0;
}
显然不可以,[ ]里必须是常量,即使加const修饰,也还是变量,只不过这个变量不可以被修改罢了。那我们是否可以使用一个指向动态开辟的空间的指针呢?这样的方法确实是可以的,但在这里会介绍一种更优的方法——柔性数组(C99中新增)。我们会在最后比较两者的优劣。
2.特点
1.结构中的柔性数组成员必须在最后一个。
2.sizeof(struct s) 返回的这种结构大小不包括柔性数组的大小。
3.包含柔性数组成员的结构用malloc ()函数进行内存的动态分配,并且分配的内存应该大于结构的大小,以适应柔性数组的预期大小。
使用示范:
struct s
**(1)Python所有方向的学习路线(新版)**
这是我花了几天的时间去把Python所有方向的技术点做的整理,形成各个领域的知识点汇总,它的用处就在于,你可以按照上面的知识点去找对应的学习资源,保证自己学得较为全面。
最近我才对这些路线做了一下新的更新,知识体系更全面了。
![在这里插入图片描述](https://img-blog.csdnimg.cn/1f807758e039481fa866130abf71d796.png#pic_center)
**(2)Python学习视频**
包含了Python入门、爬虫、数据分析和web开发的学习视频,总共100多个,虽然没有那么全面,但是对于入门来说是没问题的,学完这些之后,你可以按照我上面的学习路线去网上找其他的知识资源进行进阶。
![在这里插入图片描述](https://img-blog.csdnimg.cn/d66e3ad5592f4cdcb197de0dc0438ec5.png#pic_center)
**(3)100多个练手项目**
我们在看视频学习的时候,不能光动眼动脑不动手,比较科学的学习方法是在理解之后运用它们,这时候练手项目就很适合了,只是里面的项目比较多,水平也是参差不齐,大家可以挑自己能做的项目去练练。
![在这里插入图片描述](https://img-blog.csdnimg.cn/f5aeb4050ab547cf90b1a028d1aacb1d.png#pic_center)
**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
**[需要这份系统化学习资料的朋友,可以戳这里无偿获取](https://bbs.csdn.net/topics/618317507)**
**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**