C语言进阶(常见的动态内存错误讲解及面试题分析)

目录

前言

一、动态内存错误

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

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

3.使用free释放非动态开辟的空间

4.使用free释放动态内存中的一部分

6.动态开辟的空间忘记释放(容易造成内存泄露,比较严重)

二、动态内存错误面试题分析

1.NULL指针传参不取地址传的也是一份临时拷贝

2.局部变量和形式参数存在于栈上

3,动态内存开的空间记得free释放掉

4.非法访问内存

总结:


前言

当我们用动态内存分配函数来编写程序时,在编写的过程中常常会产生一些不易被察觉,被发现的错误,例如对NULL指针的解引用操作,对动态开辟空间的越界访问,使用free释放非动态开辟的空间,使用free释放动态内存中的一部分,对同一块动态开辟的空间,多次释放,动态开辟空间忘记释放。下面我们挨个来分析,刨析一下这些个常见的动态内存开辟的问题。

一、动态内存错误

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

代码如下(示例):

错误示例:
//动态内存开辟
int main()
{

	int* p = malloc(100000000000);
	//没有对mollac函数的返回值做判空处理
		int i = 0;
	for (i = 0; i < 10; i++)
	{
		*(p + i) = 5;
	}
	
	return 0;
}

正确示例:
//动态内存开辟
int main()
{

	int* p = malloc(100000000000);
	if (p == NULL)
	{
		return 1;
	}
		int i = 0;
	for (i = 0; i < 10; i++)
	{
		*(p + i) = 5;
	}
	for (i = 0; i < 10; i++)
	{
		printf("%d ", p[i]);
	}
	free(p);
	p = NULL;
	return 0;
}

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

代码如下(示例):

错误示例:
//动态内存开辟
int main()
{

	int* p = (int*)malloc(10*sizeof(int));
	if (p == NULL)
	{
		return 1;
	}
		int i = 0;
		//越界访问
	for (i = 0; i < 40; i++)//malloc函数只是开辟了十个整型的空间,这里却要访问四十个元素。
	{
		*(p + i) = 5;
	}
	for (i = 0; i < 40; i++)
	{
		printf("%d ", p[i]);
	}
	free(p);
	p = NULL;
	return 0;
}

正确示例:
//动态内存开辟
int main()
{

	int* p = (int*)malloc(10*sizeof(int));
	if (p == NULL)
	{
		return 1;
	}
		int i = 0;
		
	for (i = 0; i < 10; i++)
		*(p + i) = 5;
	}
	for (i = 0; i < 10; i++)
	{
		printf("%d ", p[i]);
	}
	free(p);
	p = NULL;
	return 0;
}

3.使用free释放非动态开辟的空间

代码如下(示例):

//动态内存开辟
int main()
{
	int arr[10] = { 0 };//栈区
	int* p = arr;
	free(p);//使用free释放非动态开辟的空间
	p = NULL;
	return 0;
}

4.使用free释放动态内存中的一部分

代码如下(示例):


//动态内存开辟
int main()
{
	int* p = malloc(10 * sizeof(int));
	if (p == NULL)
	{
		return 1;
	}
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		*p++ = i;//1:p一直往后走之后没人知道起始空间的位置在哪,2:p释放的只是后面空间的一部分,前面的空间并没有得到释放。
	}
	free(p);
	p = NULL;
	return 0;
}

5,对同一块动态内存动态开辟的空间多次释放

代码如下(示例):

//动态内存开辟
int main()
{
	int* p = malloc(10 * sizeof(int));
	//使用
	//释放
	free(p);
	//再次释放
	free(p);//free要是传的是空指针什么事都不会发生。
	p = NULL;
	return 0;
}

6.动态开辟的空间忘记释放(容易造成内存泄露,比较严重)

代码如下(示例):

void test()
{
	int* p = malloc(10 * sizeof(int));
	if (p == NULL)
	{
		return 1;
	}
	//使用
	//忘记释放
}

//动态内存开辟
int main()
{
	test();
	return 0;
}

二、动态内存错误面试题分析

1.NULL指针传参不取地址传的也是一份临时拷贝

例题分析:

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

程序运行结果:

拷贝不成功,程序直接挂掉。

原因分析:

str传给GetMemory函数的时候是值传递,所以GetMemory函数的形参p是str的一份临时拷贝。
在GetMemory函数内部动态申请空间的地址,存放在P中,不会影响外面str,所以当GetMemory函数返回
之后,str任然是NULL指针,所以strcpy会失败。
当GetMemory函数返回之后,形参p销毁,使得动态开辟的100个字节存在内存泄漏。

 正确代码:

第一种改法:

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


第二种改法:

char* 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.局部变量和形式参数存在于栈上

代码如下(示例):

例题分析:

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

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

程序运行结果:

打印不成功,打印的都是随机值

原因分析:

GetMemory函数内部创建的数组是在栈区上创建的
出了函数,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,动态内存开的空间记得free释放掉

代码如下(示例)

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

错误分析:

申请的动态内存空间使用完之后没有及时free释放掉。

正确代码:

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.非法访问内存

代码如下(示例)

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

错误分析:

申请的空间已经free释放还给操作系统了,及时str还记得这块空间的起始地址,但是也不能访问,属于非法访问内存空间。
free完之后要及时把str置成NULL指针。

正确代码:

void test(void)
{
	char* str = (char*)malloc(100);
	strcpy(str, "hello");
	free(str);
	str = NULL;
	if (str != NULL)
	{
		strcpy(str, "world");
		printf(str);
	}
}
int main()
{
	test();
	return 0;
}

总结:

上述给大家简单介绍了动态内存开辟常见的几种问题,也分析了往年的几道面试题里面的错误,让我们加深了对这一章的理解,后续自己使用的时候可以有效的规避掉这些问题。相信大家都学会了。如果上述文章有任何问题 ,欢迎大佬们提出质疑,我会虚心学习和改正,最重要的是能共同进步,共同成长,学习好编程。

  • 18
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 10
    评论
C语言中函数的常见错误包括: 1.函数返回值错误。函数没有正确地返回值或者返回值类型与函数声明的类型不匹配。 2.参数传递错误。函数的参数类型与函数声明的参数类型不匹配或者参数个数不正确。 3.变量作用域错误。函数内部的变量没有正确地定义或者变量名与其他变量名重复。 4.数组越界错误。在函数中访问数组时,没有正确地限制数组的大小或者数组下标越界。 5.指针错误。没有正确地初始化指针或者指针指向的地址无效。 6.内存泄漏错误。函数中没有正确地释放动态分配的内存。 7.逻辑错误。函数中的逻辑错误导致函数无法正确地实现其功能。 针对这些错误,可以进行以下改正: 1.使用正确的返回值类型和参数类型,确保函数声明和定义的一致性。 2.对于不确定的参数数量,使用可变参数函数;对于参数类型不确定的情况,使用void指针。 3.为变量定义适当的作用域,避免变量名与其他变量名重复。 4.在访问数组时,使用合适的循环和条件语句来限制数组的大小和下标。 5.在使用指针时,确保指针已经初始化且指向的地址有效。 6.在使用动态内存分配时,确保在函数结束时释放分配的内存。 7.在设计函数时,应该考虑所有可能的情况,确保函数的功能正确地实现。同时,可以使用调试工具来帮助检测和修复错误

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

K稳重

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

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

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

打赏作者

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

抵扣说明:

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

余额充值