栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

听鹏哥讲C语言的心得----6.17

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

听鹏哥讲C语言的心得----6.17

 

一、 三个存储区存储的数据种类

栈区存放 局部变量、函数的形式参数
堆区动态内存的分配
静态区存放 全局变量 、静态变量

二、动态内存的分配


目录
1.为什么要用动态内存的开辟
2.开辟和释放动态内存的函数
3.这些函数的注意事项
1.为什么要用动态内存的开辟

我目前掌握的开辟内存的方式是

1.使用变量在栈区开辟空间

int a=5;
float b=3.4;

2.使用数组在栈区开辟一段连续的空间,然后进行操作。

int arr[10]={0};

但是,针对第二种开辟方式,我们会遇到两个问题,

1)申请的内存不够用或有剩余

2)内存的长度必须是确定的

在我使用的DEV-C++和VS2013中,是不允许存在变长数组的,即以下代码是不正确的。

int n;
scanf("%d",&n);
intt arr[n];

所以,为了更合理的使用内存,我们要学习动态内存的开辟。

2.开辟和释放动态内存的函数
2.1malloc和free
void* malloc (size_t size);

这是malloc的函数原型,观察可知,它的形参是我们要开辟空间的大小,返回值是一个地址,它是该内存起始位置的地址。

如果开辟成功,则返回一个指向开辟好空间的指针。 如果开辟失败,则返回一个 NULL 指针,因此 malloc 的返回值一定要做检查。  
void free (void* ptr);
这是free 的函数原型,它的形参是指针变量,作用是释放开辟的动态内存。 2.1.1malloc和free的用法

malloc和free都声明在 stdlib.h 头文件中。


#include 
#include
int main()
{
 int* ptr = NULL;//定义一个指针变量,用来接收malloc的返回值
 ptr = (int*)malloc(num*sizeof(int));
    //num是一个整数,sizeof(int)是整形变量占的字节数(4或2,取决于编译器)
    //即开辟num个大小为4个字节的内存;
//ptr = (int*)malloc(num);这样写也是可以的
 if(NULL != ptr)//判断ptr指针是否为空
 {
 int i = 0;
 for(i=0; i 

2.2 calloc
void* calloc (size_t num, size_t size);

这是calloc的函数原型,函数的功能是为 num 个大小为 size 的元素开辟一块空间,并且把空间的每个字节初始化为0。

与函数 malloc 的区别只在于 calloc 会在 返回地址之前 把申请的空间的每个字节初始化为全0。

所以,使用malloc和calloc时,要注意区分它们的形参,和内存空间是否初始化。

2.2.1 calloc的用法
#include 
#include
int main()
{
 int* ptr = NULL;//定义一个指针变量,用来接收malloc的返回值
 ptr = (int*)calloc(num,sizeof(int));
    //num是一个整数,sizeof(int)是整形变量占的字节数(4或2,取决于编译器)
    //即为 num 个大小为 int 的元素开辟一块空间
 if(NULL != ptr)//判断ptr指针是否为空
 {
 int i = 0;
 for(i=0; i 
2.3realloc 

这是realloc的函数原型,它的行参是一个指针变量(要调整内存的地址),加一个整数(调整后的内存大小),返回值是(调整后内存的起始)地址。

void* realloc (void* ptr, size_t size);

它的作用是对已经开辟的动态内存做出调整,可以变大些(size变大),可以变小些(size变小),这取决于需求。


        对于realloc做出的调整,会出现以下两种情况:

1.原内存之后有足够的空间来完成调整

在该位置完成内存调整,原有数据不变,返还原来的起始位置

2.原内存之后没有足够的空间来完成调整

在新的区域开辟内存,原有数据复制到新内存中,同时返回新内存的起始位置。

realloc内部解决办法,如图所示

 3.这两种都是变大,但变小更好理解,就在原位置变小就可以了,就不用考虑另外再找位置开辟内存了。

 但是,必须要注意的是,针对这三种情况,尤其是前两种,考虑,返回的地址我们到底应该那个变量来接收,是之前的指针,还是新用一个指针。若用之前的,那么若开辟失败,返回的是NULL,那么原来的数据不就丢失了吗?那么若用新的指针,是不是会增添过多变量,导致使用不便?

所以,我们应该这样做,一定要判断返回值是什么。

#include 
int main()
{
 int *ptr = (int*)malloc(100);
 if(ptr != NULL)//这是申请过程的判断
 {
    //处理操作
 }
 else
 {
     exit(EXIT_FAILURE);//申请失败,结束程序
 }
 
 int*p = NULL;
 p = realloc(ptr, 1000);
 if(p != NULL)//判断一下,返回值是否为NULL,如果不是,我们就还用ptr;
 {
 ptr = p;
//业务处理
 }
else
{
printf("开辟失败!");//如果是NULL,那就开辟失败了
}
 free(ptr);
ptr=NULL;
 return 0;
 }

3.函数使用的注意事项
3.1函数返回值的判断及内存、指针的释放

通过上面的解释,我们发现,对于动态内存的申请、修改,我们必须判断能否操作成果,再来就是要及时释放(归还)内存,释放指针,这是一种良好的编程习惯。

 这种就是没有判断返回值,直接对指针使用,程序是无法运作的。

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

指针移动到了并非它所管理的内存空间,导致程序直接卡死,这种是很危险的,因为这个时候指针很有可能指向的是非法地址。

int i = 0;
 int *p = (int *)malloc(10*sizeof(int));
 if(NULL == p)
 {
 exit(EXIT_FAILURE);
 }
 for(i=0; i<=10; i++)
 {
 *(p+i) = i;//当i是10的时候越界访问,只有10个空间,指针却移动了11次,越界访问。
 }
 free(p);

看到没有,直接卡死。 

3.3对非动态开辟内存使用free释放 
int a = 10;
 int *p = &a;
 free(p);//ok?

 这样还是直接卡死

3.4使用free释放一块动态开辟内存的一部分
int *p = (int *)malloc(100);
 p++;
 free(p);//p不再指向动态内存的起始位置

 不用怀疑,还是卡死。

3.5 对同一块动态内存多次释放
int *p = (int *)malloc(100);
 free(p);
 free(p);//重复释放

 没错,它还是卡死

3.6 动态开辟内存忘记释放(内存泄漏)
int *p = (int *)malloc(100);
 if(NULL != p)
 {
 *p = 20;
 }

 诶,这种情况不会卡死,但它很危险,虽然当整个程序结束时,内存会自动释放,但是当这个程序较大时,没有释放的内存就会被一直占着,浪费空间,而且当这种浪费开始叠加时,内存早晚会被占满,所以很危险。

所以,切记要及时释放动态内存。


动态内存的开辟比数组好用,它最大的优点就是按需开辟。


以上只是对知识点说明,下次将它们运用到具体的程序中。


以上就是,我对这些内容的理解。

声明:部分代码、文案(包括部分图片)来源于比特就业科中的课件

           部分图片来源于作者本人

          

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/983821.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号