C语言之内存库函数

发布于:2022-11-29 ⋅ 阅读:(437) ⋅ 点赞:(0)

C语言之动态内存库函数

测试环境:vs2017

为何有动态动态内存

众所周知我们使用数组开辟空间十分的方便,那为什么要去动态内存中特意开辟一个空间来用于保存数据?原来,在我们使用数组开辟空间时,会经常浪费许多空间,而如果有函数可以任意在空间中申请一个大小来保存你的数据,让你的动态内存利用率得到提升,还能学到新的知识,那岂不是一石二鸟。C99标准中,我们可以使用变量来决定数组的长度,但是使用起来非常危险!!!现在的编译器大部分已经把该项功能去除了。所以我们想要得到一个可以由我们控制的空间大小的确很难,动态内存开辟函数就很好的解决了这个问题。

想要在动态内存中开辟一个空间用于保存数据,那就必须要了解一些库函数可以在动态内存中开辟空间,他们是以什么样的方式或者方法来开辟空间?开辟成功了会返回什么?开辟失败了会怎样?如果开辟空间没有释放会发生什么?还有各种各样关于动态内存函数容易忽略的问题,动态内存函数开辟空间可以越界吗?内存会被耗干吗?如果开辟好的动态内存用光了怎么办?如果开辟动态内存空间浪费太多可以缩小吗?等这些问题,希望你能在看完本章可以得到答案。

动态内存中的四个区

首先,我们先要了解内存分为四个区,栈区,堆区,静态区(常量区和全局变量区),程序代码区

栈区存放函数的参数值局部变量等临时值,由编译器自动分配和释放。

堆区一般用于动态内存开辟与释放,由程序员使用和释放,程序结束后,可能会由操作系统回收。

静态区是用于存放常量和局部变量,而且初始化和未初始化的存放在不同的区域,程序结束后,由操作系统进行释放。

程序代码区存放函数体的二进制代码。

malloc

注意:每一个函数都是在内存中开辟一段连续的空间。

在MSDN中malloc的解析:

在这里插入图片描述

malloc的使用要先引用头文件<stdlib.h>,开辟的字节数是size_t类型,表示是无符号整型,所以当我们开辟字节时,不能写入浮点数或者负数。

在这里插入图片描述

这个是malloc的返回值,动态内存开辟成功返回一个void*的指针指向开始的地址,当动态内存开辟失败返回一个空指针,你可以用void转换成一个你想要的类型,并用对应指针接收。

这里我创建三种类型不同的接收方法:

#include<stdio.h>
#include<stdlib.h>

struct Id
{
	char name[10];
	char sex[6];
	int age;
};
//整型
int main()
{
	int* p = (int*)malloc(sizeof(int) * 5);//开辟可以存放5个整型的空间
	if (p == NULL)
	{
		return 0;//直接结束
	}
	printf("数字:");
	//使用
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		*(p + i) = i;
		printf("%d ", *(p + i));
	}
	free(p);//释放
	p = NULL;
//字符
	printf("\n字符:");
	char* q = (char*)malloc(sizeof(char)*5);//开辟可以存放5个整型的空间,因为我们知道一个字符是一个字节,可直接写为5
	if (q == NULL)
	{
		return 0;
	}
	//使用
	for (i = 0; i < 5; i++)
	{
		*(q + i) = 'A' + i;
		printf("%c ", *(q + i));
	}
	free(q);//释放
	q = NULL;
//结构体
	//同样根据前两个例子我们可以知道
	struct Id* k = (struct Id*)malloc(sizeof(struct Id) * 5);//开辟可以存放5个结构体的空间
	if (k == NULL)
	{
		return 0;
	}
	//使用
	free(k);//释放
	k = NULL;
	return 0;
}

可以发现,使用起来没什么困难,然后再来看一下动态内存的变化:

这里先教大家如何打开内存条,记得要在调试模式下才可以打开内存条,进入调试模式只需按Fn+F11或者Fn+F10。

在这里插入图片描述

这里我们点击完可以看见屏幕上会多出一个窗口,我们可以进行调节,让他显示我们想要的效果。

在这里插入图片描述

这里的列可以改为我们想要的列数,可以更便于去观察内存的变化,什么输入框如果没有显示,可以拉长,然后输入你想监测的内存。

下图是p的内存,可以看到我们调试出的效果。

在这里插入图片描述

可以发现我们真的向动态内存申请存放5个整型的动态内存空间。

再观察一下动态内存的使用:

在这里插入图片描述

我们可以发现申请好的空间的确变成我们想要的数字。

接下来动态内存的释放,千万不要忘记

在这里插入图片描述

经过free释放好的动态内存应该把当初的指针置为空,因为free只是单纯地将动态内存释放,而指针依旧指着那块已经释放的空间,这是非常不安全的!

calloc

这个函数也是用于开辟动态内存的一个函数:

在这里插入图片描述

在这里插入图片描述

经过分析,比较malloc,这个函数多了一个参数,原来num是要开辟的元素个数size是要开辟的元素的大小占几个字节。其实他开辟空间的方式和malloc大同小异,经过观察内存才会发现其中的奥秘。

在使用calloc开辟的空间时:

#include<stdio.h>
#include<stdlib.h>
int main()
{
	//动态内存开辟
	int* p = (int*)calloc(5, sizeof(int));
	if (p == NULL)
	{
		return 0;
	}
	int i = 0;
	//使用
	for (i = 0; i < 5; i++)
	{
		printf("%d ", *(p + i));
	}
	//释放
	free(p);
	p = NULL;
	return 0;
}

在这里插入图片描述

在这里插入图片描述

我们发现此时此刻的内存已经发生了变化,所以calloc和malloc之间的区别就在于,calloc使用时会将申请好的内存进行初始化,而malloc不会,所以当我们开辟内存空间时可以先决定用malloc还是calloc。

realloc

该函数可以随意调节开辟空间的大小,也就是可以有**“伸缩”**功能!!不过也要注意,有时候可能会“伸缩”失败。

在这里插入图片描述

这里的memblock是一个指向初始地址的指针!他的返回值和malloc和colloc一样,都是开辟成功返回一个指向初始地址指针,开辟失败返回空。不过既然他要向内存开辟空间,是以如下图两种方法开辟的。

在这里插入图片描述

不过如果你是想将空间缩小,那肯定不会像方法二那样活动。

接下来看看是如何使用的:

#include<stdio.h>
#include<stdlib.h>
int main()
{
	int*p = (int*)malloc(sizeof(int) * 5);
	if (*p == NULL)
	{
		return 0;
	}
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		*(p + i) = i;
	}
    //增大空间
	int *ptr = (int*)realloc(p, sizeof(int) * 10);
	//开辟空间要另外创建一个指针接收,如果开辟失败则不赋值
	if (ptr != NULL)
	{
		//开辟成功,赋值
		p = ptr;
        //观察空间变化
        for (i = 5; i < 10; i++)
        {
            *(p + i) = i;
        }
	}
    
    //缩小空间
	*ptr = (int*)realloc(p, sizeof(int) * 3);
	if (ptr != NULL)
	{
		p = ptr;
	}
    free(p);
    p=NULL;
	return 0;
}

**注意增大空间时,他不会将原来的空间加上,而是开辟你定义好的字节数。**而动态内存空间就是依靠realloc来将空间伸缩,可以提升空间的利用率。

我们先看一下malloc函数

在这里插入图片描述

内存增大:

在这里插入图片描述

内存缩小:

在这里插入图片描述

看到这里我们使用时才可以放心使用,不过使用完的空间一定要记得free!!

接下来我们看两道常见的内存错误题:

//请问他会输出什么呢?存在什么错误?
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
void GetMemory(char *p)
{
p = (char *)malloc(100);
}
void Test(void)
{
char *str = NULL;
GetMemory(str);
strcpy(str, "hello world");
printf(str);
}

这段代码有很严重的指针错误,因为str已经是空指针,却还被传参,然后使用strcpy非法访问动态内存,所以该提是什么也不输出,还有很严重的内存空间开辟未释放

//请问这段代码会输出什么?存在什么错误?
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
void GetMemory(char **p, int num)
{
*p = (char *)malloc(num);
}
void Test(void)
{
char *str = NULL;
GetMemory(&str, 100);
strcpy(str, "hello");
printf(str);
}

**这段代码存在严重的内存泄漏问题,虽然会输出hello,但是内存没有得到及时释放,后期可能会有很大的麻烦!**所以使用动态内存函数任重而道远,需要多多练习,完全熟练掌控动态内存对于你们一定是不在话下!

本文含有隐藏内容,请 开通VIP 后查看

网站公告


今日签到

点亮在社区的每一天
去签到