目录
  • 前言
  • 一、动态内存错误
    • 1.对NULL指针的解引用操作
    • 2.对动态开辟空间的越界访问
    • 3.使用free释放非动态开辟的空间
    • 4.使用free释放动态内存中的一部分
    • 5.对同一块动态内存动态开辟的空间多次释放
    • 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;
    }

    程序运行结果:

    C语言 动态内存开辟常见问题解决与分析流程

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

    原因分析:

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

    程序运行结果:

    C语言 动态内存开辟常见问题解决与分析流程

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

    原因分析:

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

    总结:

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

    声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。