C语言编程C++动态内存分配示例讲解

编辑: admin 分类: c#语言 发布时间: 2021-12-12 来源:互联网
目录
  • 动态内存管理
    • 为什么存在动态内存分配
  • 动态内存函数的介绍
    • malloc申请空间和free释放空间
    • 有借有还 free释放内存
  • calloc申请内存
    • realloc调整动态内存的大小
      • realloc使用的注意事项
      • 当然realloc也可以直接开辟空间
    • 常见的动态内存错误
      • 1.对NULL指针的解引用操作
      • 2.对动态开辟空间的越界访问
      • 3.对非动态开辟内存使用free释放
      • 4.使用free释放一块动态内存开辟的一部分
      • 5.对同一块动态内存多次释放
      • 6.动态开辟内存忘记释放(内存泄漏)
    • 几个面试题
      • 题目1
      • 题目2
      • 题目3
      • 题目4
    • C/C++程序的内存开辟
      • C/C++程序内存分配的几个区域:

        动态内存管理

        为什么存在动态内存分配

        我们到现在为止掌握的是什么样的内存开辟方式呢

        //创建一个变量
        int val = 20;    //局部变量  在栈空间中开辟4个字节
        int g_val = 10;  //全局变量  在静态区中开辟4个字节
        //创建一个数组
        char arr[10] = {0}; //局部区域 在栈空间中开辟10个字节连续的空间
        char g_arr[5] = {0};//全局区域 在静态区空间中开辟5个字节的连续空间
        

        image-20210920203300323

        但是上述的开辟空间的方式有两个特点:

        1.空间开辟大小是固定的。

        2.数组在申明的时候,必须指定数组的长度,它所需要的内存在编译时分配。
        但是对于空间的需求,不仅仅是上述的情况。有时候我们需要的空间大小在程序运行的时候才能知道,那数组的编译时开辟空间的方式就不能满足了。 这时候就只能试试动态内存开辟了。

        c99是支持变长数组的,但现在很多编译器就不支持c99,连vs都不支持,所以就有动态内存的概念

        动态内存函数的介绍

        malloc申请空间和free释放空间

        c语言提供了一个动态内存开辟的函数

        void* malloc(size_t size);
        

        这个函数向内存申请一块连续可用的空间,并返回指向这块空间的指针。

        1.如果开辟成功,则返回一个指向开辟好空间的指针。

        image-20210920211643597

        2.如果开辟失败,则返回一个NULL指针,因此malloc的返回值一定要做检查。

        image-20210920213112488

        3.返回值的类型是 void ,所以malloc函数并不知道开辟空间的类型,具体在使用的时候使用者自己来决定。*

        image-20210920214320778

        4.如果参数 size 为0,malloc的行为是标准未定义的,取决于编译器。

        #include<stdio.h>
        #include<stdlib.h>
        #include<string.h>
        #include<errno.h>
        int main()
        {
        	//向内存申请10个整形的空间
        	int* p = (int*)malloc(10 * sizeof(int));
        	if (p == NULL)
        	{
        		//把开辟失败的信息打印出来
        		printf("%s",strerror(errno));
        	}
        	else
        	{
        		//正常使用空间
        		int i = 0;
        		for (i = 0; i < 10; i++)
        		{
        			*(p + i) = i;//在找下标为i的元素
        		}
        		for (i = 0; i < 10; i++)//再把每个元素打印出来
        		{
        			printf("%d ", *(p + i));
        		}
        	}
        	return 0;
        }
        

        image-20210920214641883

        那我们可不可以看开辟失败的呢

        我们可以用INT_MAX(他是整形最大),一个超级大的数字

        image-20210920215213637

        image-20210920215421502

        image-20210920215536367

        有借有还 free释放内存

        free函数用来释放动态开辟的内存。
        1.如果参数 ptr 指向的空间不是动态开辟的,那free函数的行为是未定义的。
        2.如果参数 ptr 是NULL指针,则函数什么事都不做。

        image-20210920225328673

        image-20210920230005418

        注意

        malloc和free是成对使用的,谁开辟谁释放

        calloc申请内存

        在内存中开辟一个数组,把元素都改成零

        函数的功能是为 num 个大小为 size 的元素开辟一块空间,并且把空间的每个字节初始化为0。

        image-20210921064458843

        与函数 malloc 的区别只在于 calloc 会在返回地址之前把申请的空间的每个字节初始化为全0

        image-20210921064517477

        realloc调整动态内存的大小

        当然我们可以申请空间,但会不会遇到申请的空间不够了,想要增加一些些,大了想要去掉一些些

        image-20210921070646055

        realloc使用的注意事项

        1.如果p指向的空间之后有足够的内存空间可以追加,则直接追加,后返回p

        2.如果p指向的空间之后没有足够的内存空间可以追加,则realloc函数会重新找一块新的内存区域,开辟一块满足需求的空间,并且把原来的内存中的数据拷贝回来,释放旧的内存空间,最后返回新开辟的内存空间地址

        3.但也有一个大问题,就是开辟INT_MAX,用新的变量ptr来接收realloc返回值

        image-20210921080812450

        image-20210921091309880

        当然realloc也可以直接开辟空间

        image-20210921142948715

        常见的动态内存错误

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

        #include<stdio.h>
        #include<stdlib.h>
        int main()
        {
        	int* p = (int*)malloc(40);//没成功就会有大问题
        	int i = 0;
        	for (i = 0; i < 10; i++)
        	{
        		*(p + i) = i;
        	}
        	free(p);
        	p = NULL;
        	return 0;
        }
        

        image-20210921095023767

        所以为了防止没有开辟动态内存成功就需要做个判断

        image-20210921140318273

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

        #include<stdio.h>
        #include<stdlib.h>
        #include<string.h>
        #include<errno.h>
        
        int main()
        {
        	int* p = (int*)malloc(5*sizeof(int));
        	if (p == NULL)//这里我的确判断有没有开辟成功了
        	{
        		printf("%s", strerror(errno));
        	}
        	else
        	{
        		int i = 0;
        		for (i = 0; i < 10; i++)//但是我这里访问10个整型的空间
        		{
        			*(p + i) = i;
        		}
        	}	
        	free(p);
        	p = NULL;
        	return 0;
        }
        

        image-20210921141134755

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

        int main()
        {
        	int a = 0;
        	int* p = &a;
        	*p = 20;
        	free(p);
        	p = NULL;
        	return 0;
        }
        

        image-20210921141835492

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

        #include<stdio.h>
        #include<stdlib.h>
        #include<string.h>
        #include<errno.h>
        int main()
        {
        	int* p = (int*)malloc(40);
        	if (p == NULL)
        	{
        		return 0;//如果是空指针就直接返回,不干了
        	}
        	int i = 0;
        	for (i = 0; i < 10; i++)
        	{
        		*p++ = i;//这个++就是bug的地方
        	}
        	//回收空间
        	free(p);
        	p = NULL;
        	return 0;
        }
        

        image-20210921145331118

        只要p不是指向申请的空间的首地址,其他地方都是错的

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

        #include<stdio.h>
        #include<stdlib.h>
        #include<string.h>
        #include<errno.h>
        int main()
        {
        	int* p = (int*)malloc(40);
        	if (p == NULL)
        	{
        		return 0;
        	}
        	//使用
        	//释放
        	free(p);
        	//...
        	free(p);
        	return 0;
        }
        

        image-20210921150827892

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

        #include<stdio.h>
        #include<stdlib.h>
        #include<string.h>
        #include<errno.h>
        int main()
        {
        	while (1)
        	{
        		malloc(100);
        	}
        	return 0;
        }
        

        image-20210921152924479

        几个面试题

        题目1

        void GetMemory(char* p)
        {
        	p = (char*)malloc(100);
        }
        void Test(void)
        {
        	char* str = NULL;
        	GetMemory(str);
        	strcpy(str,"hello world");
        	printf(str);//这个写法和printf("%s",str);是一样的
        }
        int main()
        {
        	Test();
        	return 0;
        }
        

        问运行Test函数会有什么样的结果

        image-20210921160444529

        修改正确

        #include<stdio.h>
        #include<stdlib.h>
        #include<string.h>
        #include<errno.h>
        void GetMemory(char* *p)
        {
        	*p = (char*)malloc(100);
        }
        void Test(void)
        {
        	char* str = NULL;
        	GetMemory(&str);
        	strcpy(str,"hello world");	
        	printf(str);//这个写法和printf("%s",str);是一样的
        	free(str);//用完就释放
        	str = NULL;
        }
        int main()
        {
        	Test();
        	return 0;
        }
        

        image-20210921161553669

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

        请问运行Test 函数会有什么样的结果

        输出随机值

        image-20210921163718477

        image-20210921164324364

        修改正确

        既然是p被销毁了,那我们让他不销毁就可以了延长它的生命周期用static

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

        image-20210921165420500

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

        这题基本和第一题一样,不过这题就只有内存泄漏的错误

        image-20210921184936339

        修改正确

        #include<stdio.h>
        #include<stdlib.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);
        	free(str);//用完就释放,防止内存泄漏
        	str = NULL;
        }
        int main()
        {
        	Test();
        	return 0;
        }
        

        image-20210921185134567

        题目4

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

        问题非常大的打印出结果

        image-20210921191535581

        image-20210921191434742

        修改正确

        #include<stdio.h>
        #include<string.h>
        #include<stdlib.h>
        
        void Test(void)
        {
        	char* str = (char*)malloc(100);
        	strcpy(str, "hello");
        	free(str);//这里考查的是free释放后并没有使str为NULL,所以下面if判断就没有作用,如果使他有作用就让str为NULL
        	str = NULL;
        	if (str != NULL)
        	{
        		strcpy(str, "world");
        		printf(str);
        	}
        }
        int main()
        {
        	Test();
        	return 0;
        }
        

        这道题真正目的就是让你什么都不打印

        image-20210921192651227

        C/C++程序的内存开辟

        image-20210921195028220

        C/C++程序内存分配的几个区域:

        栈区(stack):在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。 栈区主要存放运行函数而分配的局部变量、函数参数、返回数据、返回地址等。

        堆区(heap):一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。分配方式类似于链表。

        数据段(静态区)(static)存放全局变量、静态数据。程序结束后由系统释放。

        代码段:存放函数体(类成员函数和全局函数)的二进制代码。

        以上就是C语言编程C++动态内存分配示例讲解的详细内容,更多关于C++动态内存分配的资料请关注海外IDC网其它相关文章!

        【文章出处:香港服务器