【c语言进阶】动态内存管理知识大全(下)
创始人
2024-05-19 16:37:02
0

在这里插入图片描述

🚀write in front🚀
📜所属专栏c语言学习
🛰️博客主页:睿睿的博客主页
🛰️代码仓库:🎉VS2022_C语言仓库
🎡您的点赞、关注、收藏、评论,是对我最大的激励和支持!!!
关注我,关注我,关注我你们将会看到更多的优质内容!!

在这里插入图片描述

文章目录

  • 前言:
  • 一:经典练习题
    • 1.1:题一:
    • 1.2:题二:
    • 1.3:题三:
    • 1.4:题四:
  • 二:C/C++程序的内存区域的划分
  • 三:柔性数组:
    • 1.柔性数组的特点:
    • 2.柔性数组的使用:
    • 3.柔性数组的优势:
  • 总结:

前言:

  在上一篇文章中,我们讲解了动态内存管理的部分知识:动态内存管理知识大全(上)今天,我们先通过几道题目复习一下,并且学习柔性数组的相关知识!

一:经典练习题

1.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;
}

这段代码运行错误,原因如下:

  • GetMemory函数是传值调用,str传给p的时候,p是str的临时拷贝有自己独立的空间,当Germemory函数内部申请了空间后,地址放在p中时,str依然是NULL。当GetMemory函数返回之后,strcpy拷贝的时候,形成了非法访问内存
  • 在GetMemory函数内部,动态申请了内存,但是没有释放,会出现内存泄漏
    在这里插入图片描述
    正确写法:
//方法一:
void GetMemory(char** p)//形参用二级指针接收,此时p里面存的是str的地址
{*p = (char*)malloc(100);//*p得到str,让str指向新开辟的空间
}
void Test(void)
{char* str = NULL;GetMemory(&str);//址传递strcpy(str, "hello world");printf(str);free(str);str = NULL;
}int main()
{Test();return 0;
}//方法二:
char*  GetMemory(char* p)
{p=(char*)malloc(100);return p;
}
void Test(void)
{char* str = NULL;str=GetMemory(str);//址传递strcpy(str, "hello world");printf(str);free(str);str = NULL;
}int main()
{Test();return 0;
}

1.2:题二:

下面代码的运行结果是?

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

上面代码打印出来的是:烫烫烫烫烫烫烫烫圉7。原因如下:

  • 数组p是一个局部变量,在栈区,根据函数栈帧的知识我们知道,在出 GetMemory 函数的时候,数组 p 的内存空间就被销毁了,还给了操作系统,虽然把这个数组首元素的地址返了回去,但此时再通过地址去访问这一块空间,就成了非法访问。这种问题通常也被叫做返回栈空间地址的问题.

正确代码:

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

1.3:题三:

下面代码的运行结果是?

void 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;
}

  这段代码可以成功打印出hello,但是仔细观察就能发现,这段代码里面之见 malloc 却不见 free 这就是典型的内存泄漏

知识补充:
  肯定有很多家人们会问为什么这里可以直接printf(str),其实啊printf在打印字符串的时候,无论是括号里放进去是“…”的的字符串常量,还是一个字符指针,本质上都是传进去了首元素地址,所以我们printf(“hello”)和直接printf(str)是一样的,因为str装着hello的首元素h的地址。至于为什么以前一定要用%s打印是为了在多种类型变量一起打印时好分别开来。

正确代码:

void 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;
}

1.4:题四:

下面代码的运行结果是?

void 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;
}

这段代码可以成功打印出world。但上面这段代码是有问题的,因为我们已经把 str 给 free 掉了,意思也就是,已经把这块空间归还给操作系统了,这块空间的操作权限属于操作系统。在 free 完后没有把 str 置为空,所以 str 还是指向那块空间,此时的 str 已经变成了一个野指针,后面一些列涉及 str 的操作都属于非法访问。正确的做法是在 free 的后面,把指针置为空。

二:C/C++程序的内存区域的划分

  通过以上的题目我们发现,错误基本都是内存泄漏,非法访问,出函数该栈区被释放出现的问题。所以大家一定要弄清楚哪些在堆区,那些在栈区,这些变量会在什么时候销毁等等问题。

  • 栈区(Stack):在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。栈区主要存放运行函数而分配的局部变量、函数参数、返回数据、返回地址等。
  • 堆区(heap):由程序员分配释放管理,一般由 malloc,new等内部存储函数使用, 如果没收回,程序结束时由操作系统收回。创建堆时,一般在堆的头部 用一个字节存放堆的大小;回收堆时,通过查看这个 字节的内容,可得知需要释放的多大的内存。
  • 全局区或静态区:存放 全局变量静态变量程序结束时由系统释放,分为全局初始化区全局未初始化区
  • 常量区:存放 常量结束时由系统释放
  • 程序代码区上面4个区统称数据区:存放运行 或准备运行的程序代码,由系统调度

三:柔性数组:

  在 C99 标准中,结构中的最后一个元素的大小允许是未知大小的数组,而我们就将这个未知大小的数组称为柔性数组,并将这个数组成员称为柔性数组成员

typedef struct A
{int a;int arr1[0];//柔性数组成员
}A;typedef struct B
{int b;int arr2[];//柔性数组成员
}B;

1.柔性数组的特点:

  • 结构中的柔性数组成员前面必须至少存在一个其他成员
  • 柔性数组只能作为结构的最后一个元素,并且柔性数组的大小是不确定的。
  • sizeof 返回的这种结构大小不包括柔性数组的内存
  • 包含柔性数组成员的结构用 malloc () 函数进行内存的动态分配,并且分配的内存应该大于结构的大小,以适应柔性数组的预期大小。

例如:

typedef struct st_type
{int i;int a[0];//柔性数组成员
}type_a;
printf("%d\n", sizeof(type_a));//输出的是4

  柔性数组只能作为结构的最后一个元素,并且柔性数组的大小是不确定的。其实家人们在看sizeof()结果的时候就可以知道为什么柔性数组前面必须有其他成员了,因为sizeof返回的结构体大小不包括柔性数组的内存,所以如果没有其他成员的话,该结构体所占空间大小为0.这是不可能的。

2.柔性数组的使用:

  既然柔性数组的大小是不确定的,我们如何使用他呢?有了之前的动态内存管理函数的学习,再来研究柔性数组的使用就十分简单了,我们直接来看示例:

typedef struct test
{int i;int arr[];
}test;int main()
{test a;//定义test类型结构体atest* p = (test*)malloc(sizeof(test) + 40);//malloc函数的返回值为指针类型,故使用结构体指针test*//使用malloc函数动态分配空间:test类型结构体的大小(不包含柔性数组) + 40字节if (p == NULL){perror("malloc");//判断动态内存空间是否开辟成功return 1;}//业务处理1:p->i = 10;int i = 0;//给柔性数组元素赋值:for (i = 0; i < p->i; i++){p->arr[i] = i;}//打印柔性数组元素:for(i=0;ii;i++){printf("%d ", p->arr[i]);}printf("\n");test* pp = (test*)realloc(p, sizeof(test) + 80);//使用realloc函数将结构指针指向的结构a进行扩容if (pp == NULL){perror("realloc");//判断动态内存空间是否扩容成功return 1;}//业务处理2:pp->i = 20;for (i = 0; i < pp->i; i++){pp->arr[i] = i + 9;}for (i = 0; i < pp->i; i++){printf("%d ", pp->arr[i]);}printf("\n");free(pp);pp = NULL;return 0;
}

在这个示例中,我们首先定义了 test 类型结构体 a,接着使用了 malloc 函数为结构体中的 i与柔性数组分配了动态存储空间;接着在判断非空(动态内存空间分配成功)后给结构体成员 i 与柔性数组 arr 内元素赋值,并进行了打印;再接下来,我们通过使用 realloc 函数包含柔性数组 arr 的结构体 a 扩容,并在扩容后给结构体内各成员重新赋值并打印;最后释放使用完毕的动态内存空间并将指针置空:
在这里插入图片描述

3.柔性数组的优势:

  但是同时我们又发现,我们使用柔性数组的目的在于希望使结构成员的空间变为动态,可大可小,那么为什么我们不将其写成指针由指针成员来指向其它的空间呢?如下:

typedef struct test
{int i;int* p;
}test;int main()
{test* ptr = (test*)malloc(sizeof(test));if (ptr == NULL){perror("malloc");return 1;}ptr->p = (int*)malloc(40);if (ptr->p == NULL){free(ptr->p);p=NULL;free(ptr);ptr = NULL;return 1;}return 0;
}

  在这里我们发现,虽然两段代码效果一样,但是大家会发现,柔性数组的只用开辟一次空间,而第二个需要开辟两次!并且释放也是一样。
优点:

  • 方便内存释放
  • 有利于提升访问速度
  • 有利于减少内存碎片

总结:

  加上今天内容的学习,我们就可以将动态内存空间灵活的运用在我们的程序代码之中了。并且通过使用并管理动态内存空间,还可以提高我们代码及程序的灵活性和执行效率,有助于我们写出更加优秀的程序。
  更新不易,辛苦各位小伙伴们动动小手,👍三连走一走💕💕 ~ ~ ~ 你们真的对我很重要!最后,本文仍有许多不足之处,欢迎各位认真读完文章的小伙伴们随时私信交流、批评指正!

专栏订阅:
每日一题
c语言学习
算法
智力题
更新不易,辛苦各位小伙伴们动动小手,👍三连走一走💕💕 ~ ~ ~ 你们真的对我很重要!最后,本文仍有许多不足之处,欢迎各位认真读完文章的小伙伴们随时私信交流、批评指正!

在这里插入图片描述

相关内容

热门资讯

《形神拳》说课稿 《形神拳》说课稿  一、指导思想、理论依据:  本课以新的课程标准为依据,倡导主体性与研究性的学习,...
晚会主持稿 晚会主持稿范文(通用11篇)  在充满活力,日益开放的今天,很多地方都会使用到主持稿,主持稿是以节目...
国旗下讲话稿 国旗下讲话稿(通用20篇)  在我们平凡的日常里,我们很多时候都不得不用到讲话稿,讲话稿一般是开会前...
新闻稿标题怎么写 新闻稿标题怎么写  新闻稿异于新闻,新闻稿是鼓励新闻记者在该题材上撰写的稿件。那么新闻稿该如何起一个...
校园文化艺术节动员大会发言稿 校园文化艺术节动员大会发言稿(通用13篇)  随着社会不断地进步,发言稿使用的情况越来越多,发言稿在...
校园新闻稿 校园新闻稿范文校园活动进行后,写一份新闻稿是为了更好的记录下这个活动的经过,校园新闻稿范文。校园新闻...
记承天寺夜游说课稿 导语:以下是相关的内容范文,欢迎大家前来借鉴!更多的内容请关注大学网!《记承天寺夜游》说课稿一、 说...
幸福的大桌子说课稿 幸福的大桌子说课稿  作为一位优秀的人民教师,往往需要进行说课稿编写工作,说课稿可以帮助我们提高教学...
精选情感电台广播稿   本站为大家精选了精选情感电台广播稿,欢迎阅读。  精选情感电台广播稿【一】  大家好,收音机旁我...
致接力运动员加油稿   致接力运动员加油稿(一)  小小接力棒,连着你和我,牵着你的情,动着我的心。这是团结的象征,这是...
致敬最美逆行者讲话稿 致敬最美逆行者讲话稿范文(精选11篇)  随着社会一步步向前发展,各种讲话稿频频出现,讲话稿可以按照...
《观潮》说课稿 《观潮》说课稿15篇  作为一位杰出的教职工,总不可避免地需要编写说课稿,说课稿有助于提高教师的语言...
八年级下册《马说》说课稿 八年级下册《马说》说课稿八年级下册《马说》说课稿1  一.说教材  1. 教材所处的地位和作用:  ...
小学数学说课例稿   实际上数学就在学生身边,教师要善于引导学生运用数学的眼光去观察和认识现实生活中的客观事物,在有关...
《放小鸟》说课稿 《放小鸟》说课稿  作为一名为他人授业解惑的教育工作者,时常需要用到说课稿,借助说课稿可以更好地提高...
《平行四边形的认识》数学说课... 《平行四边形的认识》数学说课稿  作为一位兢兢业业的人民教师,时常要开展说课稿准备工作,借助说课稿可...
职工拾金不昧新闻稿 职工拾金不昧新闻稿  拾金不昧拾到东西并不隐瞒下来据为己有,指良好的人的道德和社会风尚。以下是小编为...
《鸬鹚》优秀说课稿 《鸬鹚》优秀说课稿  《鸬鹚》是义务教育六年制小学语文第七册第21课,作者是我国现代著名的文学家郑振...
校园红领巾广播稿 校园红领巾广播稿100字(通用27篇)  有在广播站锻炼的同学们,广播前一般都会提前做好广播稿,优秀...
《最后一课》说课稿 《最后一课》说课稿《最后一课》说课稿1  它是初中一年级的教学内容,具体编排在第一册第二单元的第二篇...