C语言中动态内存的开辟

本文详细介绍了C/C++中malloc、calloc、realloc和free等动态内存管理函数的使用,以及它们之间的异同,特别强调了配合free释放内存的重要性,并讨论了柔性数组的概念、特点和优势。
摘要由CSDN通过智能技术生成

目录

1.前言

2.相关头文件

3.库函数介绍和使用

1.malloc框架

2.calloc框架

·malloc和calloc异同点

1.相同点

2.不同点

3.free框架

4.malloc的使用

·笔记

5.calloc的使用

6.realloc框架

7.realloc的使用

·realloc在调整内存空间存在两种情况

4.常见的动态内存错误

5.C/C++程序的内存开辟​

6.柔性数组

1.柔性数组的概念

2.柔性数组的特点

3.柔性数组的使用和优势

·柔性数组的优势之处


1.前言

        动态内存概括为在我们运行程序中可以更改的内存,C语言中提供malloc、calloc、realloc动态内存开辟的库函数,以及必不可少的free内存释放函数,它们能够使内存利用得当,甚至灵活的调整内存大小,但是也存在必然的隐患,不过是可以排查消除的

        内存五大分区 按照内存地址从高(0xffffffff)到低(0x00000000)的顺序排列,可分为5大分区:栈区 -> 堆区 -> 全局静态区 -> 常量区 -> 代码区。

        动态内存的开辟是在堆区上,我们平常大部分用的局部变量、函数参数等运行时分配、系统自动管理的是在栈区上,所以这可能对你来说是一篇温故而知新的知识,也可能对你来说是一个了如指掌的知识碎片,所以我会简单大致的概括、通俗易懂的讲解、直击重点的细节,多少会让你有些收获。

---------------------------------------------------------------------------------------------------------------------------------

2.相关头文件

#include <stdio.h>      /* printf, scanf, NULL */
#include <stdlib.h>     /* malloc, realloc, calloc, free */

--------------------------------------------------------------------------------------

3.库函数介绍和使用

首先,是关键的库函数malloc和calloc!

1.malloc框架

void* malloc (size_t size);

2.calloc框架

void* calloc (size_t num, size_t size);

---------------------------------------------------------------------------------------------------------------------------------

malloc和calloc异同点

1.相同点

            1.无符号整形(size_t)和size组成的含义是所要开辟内存的大小,以字节为单位

            2.返回值void*表示适用于所有类型的指针来接收并且需要强制类型转换来适应所要接受的类型指针

            3.如果开辟成功返回一个指向开辟好空间的指针
               如果开辟失败返回一个NULL指针因此malloc、calloc以及realloc的返回值一定要做检查(例如设置一个指针来存放返回的地址,判断是否为NULL,如果不为NULL,再使用要使用的指针来存放,这样可以避免本身要使用的指针值改变)。

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

---------------------------------------------------------------------------------------------------------------------------------

2.不同点

            1.malloc开辟的空间是未初始化的,calloc默认初始化为0,单位为字节

            2.calloc中num参数表示要开辟num个size大小的字节,比如calloc(3,4);表示开辟3个大小为4个字节的内存块,可以使用在数组中

            3.相比于malloc,calloc的初始化显得更胜一筹,对于使用建议是calloc

---------------------------------------------------------------------------------------------------------------------------------

3.free框架

void free (void* ptr);

重点!free,free,free!再使用malloc等一系列动态内存开辟相关库函数是一定!一定! 一定要配合free来使用!

---------------------------------------------------------------------------------------------------------------------------------

4.malloc的使用

综上我们来串代码尝尝鲜(非常重要哦,理论与实践相结合哦)~~~综上我们来串代码尝尝鲜(非常重要哦,理论与实践相结合哦)~~~

#include <stdio.h>
#include <stdlib.h>
int main()
{
	int* ptr = (int*)malloc(40);
                    //因为这里我们只做演示,对于有价值的指针需要新开一个指针来存放且判断从而再将
		            //malloc开辟的指针放入这个有价值的指针
	if (ptr == NULL)
	{
		perror("malloc"); 
			//或者printf("%s",strerror(errno));  不过strerror头文件是string.h  errno头文件是errno.h
			//相比你也能看出来perror更简洁方便,没错,它的使用是在终端上打印""双引号里面的内容+一个:冒号+错误信息
			//直接打印使用perror,如果只想要错误码就使用strerror(errno),因为error存放的是程序的错误码,
			//strerror是将错误码转换成字符串,不以%s字符串的形式打印就相当于得到了一个指针,指向这个错误信息。
		return 1;
			//设置返回值,防止继续执行,这个是必不可少的。
	}
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", *(ptr + i));
	}
	//必不可少的使用后free 和 NULL置空
	free(ptr);
	ptr = NULL;
	return 0;
}

笔记

其中知识点:perror、strerror与错误码相关

易错点:忘记判断NULL忘记free和指针置空

free释放(返还)空间,但不会置为空指针,防止它是野指针、防止内存泄漏(不用,占用空间),需要手动置为空指针,所以记住开辟堆区空间的库函数要和free要一起使用

扩展:free不释放等程序结束也能自动回收内存空间

5.calloc的使用

#include <string.h>
#include <errno.h>
int main()
{
	int* ptr = calloc(10, 4);
	if (ptr == NULL)
	{
		printf("%s", strerror(errno));
		return 1;
	}
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", *(ptr + i));
	}
	free(ptr);
	ptr = NULL;
	return 0;
}

---------------------------------------------------------------------------------------------------------------------------------其次,是重头戏realloc,这位更是重量级

6.realloc框架

void* realloc (void* ptr, size_t size);

 1.ptr 是要调整的内存地址
 2.size 调整之后新大小
 3.返回值为调整之后的内存起始位置。

7.realloc的使用

代码不能停,感受它的用处

#include <stdio.h>
#include <stdlib.h>
int main()
{
	int* p = (int*)calloc(10, sizeof(int));

	if (p == NULL)
	{
		perror("calloc");
		return 1;
	}

	//realloc从新分配大小为20个字节
	int* ptr = (int*)realloc(p, 20 * sizeof(int));
	if (ptr != NULL)
	{
		p = ptr;
	}

	//free释放
	free(p);
	p = NULL;
	return 0;
}

realloc在调整内存空间存在两种情况

1.原有空间有足够大的空间;

2.原有空间没有足够大的空间。
总结一句话:realloc开辟空间,后面空间足够时还是用原首地址,扩容(直接追加),空间不足够,会开辟一块新的空间,一次找一块连续的空间,把原来的内容拷贝到新的地址内,此时地址改变,如果找不到合适的空间来调整大小,这时返回空指针,所以不用原来的指针变量来接收值,防止值被改变

---------------------------------------------------------------------------------------------------------------------------------

4.常见的动态内存错误

有下面两串代码尝尝鲜

//动态内存忘记释放、内存泄漏
int main()
{
	int* p = (int*)malloc(100);
	int flag = 0;
	scanf("%d", &flag);//5
	if (flag == 5)
		return 1;//!!!
	free(p);
	p = NULL;
	return 0;
}
int* test()
{
	int* p = (int*)malloc(100);
	if (p == NULL)
	{
		return p;//1.避免对空指针解引用
	}
	return p;
}
int main()
{
	int* ret = test();
	//忘记释放开辟的动态内存
	return 0;
}

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

 C/C++程序内存分配的几个区域
1. 栈区(stack):在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结
束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是
分配的内存容量有限。 栈区主要存放运行函数而分配的局部变量、函数参数、返回数据、返
回地址等。
2. 堆区(heap):一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。分
配方式类似于链表。
3. 数据段(静态区)(static)存放全局变量、静态数据。程序结束后由系统释放。
4. 代码段:存放函数体(类成员函数和全局函数)的二进制代码。
有了这幅图,我们就可以更好的理解在《C语言初识》中讲的static关键字修饰局部变量的例子了。
实际上普通的局部变量是在栈区分配空间的,栈区的特点是在上面创建的变量出了作用域就销毁。
但是被static修饰的变量存放在数据段(静态区),数据段的特点是在上面创建的变量,直到程序
结束才销毁,所以生命周期变长。

6.柔性数组

1.柔性数组的概念

C99 中,结构中的最后一个元素允许是未知大小的数组,这就叫做『柔性数组』成员
利用柔性数组可以灵活开辟复杂对象的动态内存大小,有效的合理分配内存,这与动态内存相关,所以避免不了free对动态内存开辟的指针进行释放且手动置为NULL空指针。

2.柔性数组的特点

        1.结构体中最后一个成员是个数组数组元素个数为空或者是0。

        2.结构中的柔性数组成员前面必须至少一个其他成员

        3.sizeof 返回的这种结构大小不包括柔性数组的内存
        4.包含柔性数组成员的结构用malloc ()函数进行内存的动态分配,并且分配的内存应该大于结构的大小,以适应柔性数组的预期大小。

3.柔性数组的使用和优势

代码1是柔性数组的使用

Struct S
{
    int i;
    int arr[0];
}
int main()
{
	int i = 0;
	S* p = (S*)malloc(sizeof(S) + 100 * sizeof(int));

	p->i = 100;
	for (i = 0; i < 100; i++)
	{
	    p->arr[i] = i;
	}
	free(p);
    return 0;
}

代码2是动态内存开辟的指针

typedef struct S
{
	int i;
	int* i2;
}S;
int main()
{
	S* p = (S*)malloc(sizeof(S));
	p->i = 100;
	p->i2 = (int*)malloc(p->i * sizeof(int));

	int i = 0;
	for (i = 0; i < 100; i++)
	{
		p->i2[i] = i;
	}
	//释放空间
	free(p->i2);
	p->i2 = NULL;
	free(p);
	p = NULL;
	return 0;
}

柔性数组的优势之处

代码1的好处是方便内存释放,一次性分配内存,例如用户使用代码2时不知道需要释放两次,代码1用户做一次free就可以把所有的内存释放掉。其次,是有利于提高访问速度,因为是内存的开辟是连续的,也有益于减少内存碎片(没有被使用的一些内存)。

  • 45
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 17
    评论
回答: 当使用malloc函数开辟空间时,需要注意几个问题。首先,需要使用sizeof运算符来确定所需分配的内存空间的大小。例如,可以使用sizeof(float)来确定分配一个float类型变量所需的内存空间大小。\[1\]其次,需要注意malloc函数在堆上分配内存空间,而不是在栈上分配。这意味着在函数调用结束后,栈上的内存空间会被清理,但堆上的内存空间只有在程序结束后才会被释放。因此,需要使用free函数来释放通过malloc分配的内存空间,以避免内存泄漏。\[2\]最后,如果需要分配自定义结构体等长度未知的内存空间,通常需要将malloc与sizeof一起使用,以确定所需分配的内存空间的大小。\[3\] #### 引用[.reference_title] - *1* *3* [使用malloc函数分配空间](https://blog.csdn.net/qq_46227775/article/details/119992035)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [C语言_malloc动态开辟内存空间](https://blog.csdn.net/weixin_54859557/article/details/125706866)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值