动态内存中关于GetMemory函数的几道问题

学习完动态管理内存这部分内容之后,我们对它在实际问题中的应用可能还不是很了解,通过这几个例子我们来进一步巩固动态内存的相关知识。


目录

1.非法访问以及内存泄漏问题

1.1错误实例

1.2正确代码

2.返回栈空间地址,非法访问问题

2.1错误示例

2.2正确代码

3.野指针被使用的问题

3.1错误代码

3.2正确代码


1.非法访问以及内存泄漏问题

1.1错误实例

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>

void GetMemory(char* p)
{
	 p = (char*)malloc(sizeof(char) * 100);
 	if (p == NULL)
	{
		perror("malloc");
		return ;
	}
}
void Test(void)
{
	char* str = NULL;
	GetMemory(str);
	strcpy(str, "hello world!");
	printf(str);
}
int main()
{
	Test();
	return 0;
}

我们会发现这个代码运行不出来

为什么会这样,我们来仔细分析一下:

这个代码存在两个问题

1.调用函数GetMemory时,我们传入str,这里传的是str本身,属于值传递,指针p得到的是str的值NULL,使用malloc函数后,假设开辟好的空间的首地址为0f12ff40,p指向这块空间的首地址,但是这与str无关,p是str的一份临时拷贝,有自己独立的空间,str的值依然为NULL,当GetMemory函数运行结束时,p被销毁,空间回收,malloc开辟好的空间我们无法得知在哪,我们没有机会释放malloc开辟的空间,存在内存泄漏

2.同时,我们在销毁了指针p后,依旧使用这块空间,对它进行strcpy,这是存在非法访问

1.2正确代码

我们有两种改正方法

1.第一种:我们以前学到过,修改一级指针要用二级指针,即用二级指针来接收一级指针的地址,同时,使用完这块空间后要进行释放

//正确代码
// 
//第一种改正
void GetMemory(char** p)
{
	*p = (char*)malloc(sizeof(char) * 100);
 	if (*p == NULL)
	{
		perror("malloc");
		return ;
	}
}
void Test(void)
{
	char* str = NULL;
	GetMemory(&str);//我们在这里传入一级指针的地址,用二级指针来接收
	strcpy(str, "hello world!");
	printf(str);
	//使用完之后我们要进行空间释放
	free(str);
	str = NULL;
}
int main()
{
	Test();
	return 0;
}

2.第二种:我们给GetMemory函数一个返回值,这样执行这个函数结束时,p指针不会被销毁,同时,使用完这块空间后要进行释放

//第二种改正
char* GetMemory(char* p)
{
	p = (char*)malloc(sizeof(char) * 100);
	if (p == NULL)
	{
		perror("malloc");
		return 1;
	}
	return p;
}
void Test(void)
{
	char* str = NULL;
	str=GetMemory(str);//我们用指针str来接收函数返回值
	strcpy(str, "hello world!");
	printf(str);
	//使用完之后我们要进行空间释放
	free(str);
	str = NULL;
}
int main()
{
	Test();
	return 0;
}

如果认真看了我们写的代码,你可能会疑惑printf(str)这种输出函数的写法真的正确吗,我们下面来分析一下:

int main()
{
	char* str = "abcdef";//这里str指向的是字符串的首地址也就是a的地址
	printf("%s\n", str);

	printf("abcdef\n");//这里打印时,传给printf函数的其实是字符串首地址a的地址
	
	printf(str);//所以我们这种写法是完全正确的,str中放的是a的地址,printf函数
	      //接收的也是a的地址
	return 0;
}

2.返回栈空间地址,非法访问问题

2.1错误示例

char* GetMemory(void)
{
	char p[] = "hello world!";
	return p;//返回栈空间地址的问题
}
void Test(void)
{
	char* str = NULL;
	str = GetMemory();
	printf(str);
}
int main()
{
	Test();
	return 0;
}

我们分析一下这个代码:

p数组中存放“hello world!\0”,return p时,返回首字符的地址即h的地址,但同时返回这个首地址之后也释放了这块空间,归还了操作系统,我们没有权限使用这块空间,但是str接收了这块空间还打印了str,属于非法访问,越界访问

2.2正确代码

这种也有两种改法:

1.想要使用这块空间,我们需要把数组p变为指针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;
}

2.把数组p变成静态区的变量,那么这块空间就不会被释放掉,里面存入的字符串也可以被打印

//第二种

char* GetMemory(void)
{
	static char p[] = "hello world!";//存入静态区
	return p;
}
void Test(void)
{
	char* str = NULL;
	str = GetMemory();
	printf(str);
}
int main()
{
	Test();
	return 0;
}

3.野指针被使用的问题

3.1错误代码

//野指针被使用

void Test(void)
{
	char* str = (char*)malloc(sizeof(char) * 100);
	if (str == NULL)
	{
		perror("malloc");
		return;
	}
	strcpy(str, "hello");
	printf(str);
	free(str);

	if (str != NULL)
	{
		strcpy(str, "world!");
		printf(str);
	}
}
int main()
{
	Test();
	return 0;
}

我们查看代码,发现str被free后依旧被使用,此时,str是一个野指针,str指向的这块空间被释放,但是我们在释放后还想通过str这个指针来使用这块空间打印world!,这就形成了非法访问,我们以前就说过,虽然可以打印出来,但这是违法的,不安全

3.2正确代码

解决这个问题我们也有两种方法:

1.我们在free(str)后,应该把str主动置为NULL,确保不会形成野指针

//正确代码
void Test(void)
{
	char* str = (char*)malloc(sizeof(char) * 100);
	if (str == NULL)
	{
		perror("malloc");
		return;
	}
	strcpy(str, "hello");
	printf(str);
	free(str);
	str = NULL;
	//想要使用的话可以在重新开辟一块空间
	if (str != NULL)
	{
		strcpy(str, "world!");
		printf(str);
	}
}
int main()
{
	Test();
	return 0;
}

2.在整体使用完之后再释放这块空间


void Test(void)
{
	char* str = (char*)malloc(sizeof(char) * 100);
	if (str == NULL)
	{
		perror("malloc");
		return;
	}
	strcpy(str, "hello");
	printf(str);
    strcpy(str, " world!");
	printf(str);
	//使用完之后在释放
	free(str);
	str = NULL;
}
int main()
{
	Test();
	return 0;
}


这就是我们应用动态开辟内存函数常遇到的问题,其实,只要大家书写时细心,这些问题是可以避免的,本期分享就到这里,大家下期再见!!!

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

月亮夹馍干

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值