常见的动态内存错误和几个经典的笔试题

常见的动态内存错误和几个经典的笔试题,第1张

目录

一. 常见的动态内存错误

1.对NULL指针的解引用 *** 作

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

3.对非动态开辟内存使用free释放

4.使用free释放一块动态开辟内存的一部分

 5.对同一块动态内存多次释放

6.动态开辟内存忘记释放(内存泄漏)

二、几个经典的笔试题

1.题目1

2.题目2

3.题目3

4.题目4


一. 常见的动态内存错误 1.对NULL指针的解引用 *** 作
int main()
{
    int *p = (int *)malloc(INT_MAX/4);
    *p = 20;//如果p的值是NULL,就会有问题
    free(p);
    return 0;
}

此代码是有问题的,我们不知道malloc函数返回值是不是空指针,如果开辟失败,则返回空指针,假设开辟失败,p为空指针(NULL),对p进行解引用 *** 作会产生问题(空指针不能进行解引用 *** 作)

改进:对p进行一个判断,判断怕是否为NULL

#include 
#include 
#include 

int main()
{
	int* p = (int*)malloc(INT_MAX / 4);
	if (p == NULL)
	{
		perror("malloc");
		return 1;
	}
	else
	{
		*p = 20;
	}
	free(p);
	return 0;
}

 总结:在进行解引用 *** 作时,要对malloc函数返回值进行合理的判断(空指针不能进行解引用 *** 作)

2.对动态开辟空间的越界访问
int main()
{
	int*p = (int*)malloc(20);
	if (p == NULL)
		return 1;
	//使用
	int i = 0;
	for (i = 0; i < 20; i++)
	{
		*(p + i) = i;
		//p[i] = i;
	}
	//释放
	free(p);
	p = NULL;

	return 0;
}

这样的代码是有问题的,会造成越界访问。

for (i = 0; i < 20; i++)
	{
		*(p + i) = i;
		//p[i] = i;
	}

总共开辟20个字节,for一共循环20次,每次一个整形指针+i,循环20次超越边界,形成了越界访问

改进:最大只能访问5个整形(i < 5)

int main()
{
	int*p = (int*)malloc(20);
	if (p == NULL)
		return 1;
	//使用
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		*(p + i) = i;
		//p[i] = i;
	}
	//释放
	free(p);
	p = NULL;

	return 0;
}
3.对非动态开辟内存使用free释放
int main()
{
	int num = 10;
  int* p = #

	//.....

	free(p);
	p = NULL;

	return 0;
}

程序代码有问题:非动态开辟内存使用free释放(此方法是不对的,代码就是错误的)比如嘴角起个泡,非要往脚趾头抹药——牛头不对马嘴

4.使用free释放一块动态开辟内存的一部分
int main()
{
	int* p = (int*)malloc(40);
	if (p == NULL)
		return 1;
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		*p = i;
		p++;
	}
	//释放
	//在释放的时候,p指向的不再是动态内存空间的起始位置
	free(p);
	p = NULL;

	return 0;
}

  显然,代码是有问题的,重视空间管理(起始位置不可变),显然在释放的时候,p指向的不再是动态内存空间的起始位置

如果想要变位置,可以使用另外一个指针来改变位置,切记起始位置不可变

 5.对同一块动态内存多次释放
int main()
{
	int* p = (int*)malloc(40);
	if (p == NULL)
		return 1;
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		*(p + i) = i;
	}
	
	free(p);




	free(p);

	return 0;
}

 此代码有错误,错误之处在于对同一块动态内存多次释放

改进:可以在第一个free后边加(p = NULL),因为free之后置为空,对空指针释放就相当于什么事没干

进而引出每次free之后置为NULL很重要!!!

int main()
{
	int* p = (int*)malloc(40);
	if (p == NULL)
		return 1;
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		*(p + i) = i;
	}

	free(p);
	p = NULL;



	free(p);

	return 0;
}
6.动态开辟内存忘记释放(内存泄漏)
int* get_memory()
{
	int* p = (int*)malloc(40);
	//....
	return p;
}


int main()
{
	int *ptr = get_memory();
	//使用


	return 0;
}

此代码是错误的,动态开辟内存忘记释放(内存泄漏),不可以只顾申请,不释放

改进:函数会返回动态开辟空间的地址,记得在使用之后返回

//函数会返回动态开辟空间的地址,记得在使用之后返回
int* get_memory()
{
	int* p = (int*)malloc(40);
	//....
	return p;
}


int main()
{
	int *ptr = get_memory();
	//使用

	//释放
	free(ptr);
	ptr = NULL;

	return 0;
}

总结:忘记释放不再使用的动态开辟的空间会造成内存泄漏。
切记:动态开辟的空间一定要释放,并且正确释放 。

二、几个经典的笔试题 1.题目1
#include 

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

此代码的问题:1.str里边还是NULL,拷贝不成功。strcpy:把 hello world 放到str中,必须对str进行解引用 *** 作,而空指针不可以解引用 *** 作

2.没有释放空间,造成内存泄漏

改进:

1.GetMemory函数帮忙申请100个字节的空间,最好把100个空间的地址放到str中,这样strcpy就可以把hello world拷贝到刚刚开辟的100个空间中

我们希望把malloc中开辟的100个字节的空间放到str中,GetMemory函数会改变str,需要把地址放到str中去改变str,GetMemory函数最好传str的地址(&str),如果不传地址,值传过去,p是str的一份临时拷贝,p的修改不会改变str

因为str是一个char*类型,&str就是二阶指针,对于p来说就是char** p(void GetMemory(char** p)),p得到的是str的地址,如果通过p找到str,则对p解引用(*p = (char*)malloc(100))

2.释放空间:free(str); str = NULL;

void GetMemory(char** p)
{
	*p = (char*)malloc(100);
}

void Test(void)
{
	char* str = NULL;
	GetMemory(&str);
	strcpy(str, "hello world");
	printf(str);
	//释放
	free(str);
	str = NULL;
}

int main()
{
	Test();
	return 0;
}

2.题目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;
}

代码有问题:p是一个局部数组,进入函数创建,出函数销毁,所谓的销毁就是hello world还给 *** 作系统,地址不变

printf(str) :通过str内容找原来的空间(hello world),但是空间已经被销毁(没有使用权限),有可能被覆盖等等,如果坚持打印内容,抱歉,代码出问题

这是一个返回栈空间地址的问题

例如:

int test()
{
	int a = 10;
	return a;
}

int main()
{
	int ret = test();
	printf("%d\n", ret);

	return 0;
}

代码没有问题

 return a:返回栈空间的变量,没有返回地址

出范围销毁,在销毁之前会找一个寄存器,把10先放到寄存器中,然后销毁,返回之后再把寄存器里的值放到ret中

例如:

int* test()
{
	int a = 10;
	return &a;
}

int main()
{
	int*p  = test();
	printf("hehe\n");
	printf("%d\n", *p);

	return 0;
}

代码有问题:返回栈空间地址的问题

分析:创建变量a = 10,假设a的地址为0x0012ff40,把地址返回去,给了p,p为指针变量,里边接收到了地址,a的地址返回确实放到了p中,p中的地址有能力找到a的空间,但是出了函数空间,a已经不存在了,空间已经不属于a了,p中记住的地址已经没用了,去找原来的空间已经不存在了,此时p已经为野指针了,如果p通过地址访问原来的空间,则为非法访问

3.题目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);

	free(str);
	str = NULL;
}

int main()
{
	Test();
 //......
	return 0;
}

出现内存泄露的问题,没有释放空间

4.题目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;
}

代码有问题:str是野指针,这里就是非法访问

改进:free之后,一定要把指针置为空,否则指针就为野指针

void Test(void)
{
	char* str = (char*)malloc(100);
	if (str == NULL)
		return;

	strcpy(str, "hello");
	free(str);
	str = NULL;

	if (str != NULL)
	{
		strcpy(str, "world");
		printf(str);
	}
}

int main()
{
	Test();
	return 0;
}

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/3001807.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-09-27
下一篇 2022-09-27

发表评论

登录后才能评论

评论列表(0条)

保存