IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C语言 动态内存管理 -> 正文阅读

[C++知识库]C语言 动态内存管理

1.动态内存函数

1.malloc函数和free函数

malloc函数的函数声明:

void* malloc (size_t size);

malloc函数和free函数的头文件都是

#include <stdlib.h>

malloc函数向内存申请一块连续可用的空间,并返回指向这块空间的指针。
如果开辟成功,则返回一个指向开辟好空间的指针。
如果开辟失败,则返回一个NULL指针,因此malloc的返回值一定要做检查。
返回值的类型是 void* ,所以malloc函数并不知道开辟空间的类型,具体在使用的时候使用者自己
来决定。
如果参数 size 为0,malloc的行为是标准是未定义的,取决于编译器

free函数是专门做动态内存的回收和释放的。

free的函数声明为:

void free (void* ptr);

对于free函数:
如果参数 ptr 指向的空间不是动态开辟的,那free函数的行为是未定义的。
如果参数 ptr 是NULL指针,则函数什么事都不做。

(free函数在后面的动态内存函数中也会用到,其效果和使用方法是一样的)

举一个关于malloc函数和free函数的例子

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include <stdlib.h>//malloc和free的头文件
#include <string.h>
#include <errno.h>
int main()
{
	int arr[10] = { 0 };
	int* p = (int*)malloc(40);//返回值为void* 要自己设置类型
	//开辟成功则返回一个指向开辟好空间的指针
	//开辟失败则返回NULL指针
	if (p == NULL)
	{
		printf("%s\n", strerror(errno));
		return 1;
	}
	//使用
	int i;
	for (i = 0; i < 10; i++)
	{
		*(p + i) = i;
	}
	for (i = 0; i < 10; i++)
	{
		printf("%d\n", *(p + i));
	}
	//释放掉内存
	free(p);//动态内存的释放和回收
	//若free释放的不是动态内存 情况未定义
	p = NULL;
	return 0;
}

2.calloc函数

calloc函数也是用来动态内存分配,它的用法与malloc相似,但是多了一个参数,并且它可以将开辟出来的空间初始化,其作用相当于malloc函数加上memset函数的结合体。

calloc的函数声明为:

void* calloc (size_t num, size_t size);

calloc函数的功能是:

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

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

举个例子:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
int main()
{
	int* p = (int*)calloc(10, sizeof(int));//calloc会将开辟出来的内存初始化成0
	if (p == NULL)
	{
		printf("%s\n", strerror(errno));
		return 1;
	}
	//打印
	int i;
	for (i = 0; i < 10; i++)
	{
		printf("%d\n", *(p + i));
	}
	//释放
	free(p);
	p = NULL;
	return 0;
}

如果对开辟出来的空间又初始化为0的需求 那用calloc函数在合适不过了。

3.realloc函数

它的函数声明为:

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

realloc函数的出现让动态内存管理更加灵活。
有时会我们发现过去申请的空间太小了,有时候我们又会觉得申请的空间过大了,那为了合理的时
候内存,我们一定会对内存的大小做灵活的调整。那 realloc 函数就可以做到对动态开辟内存大小
的调整。
ptr 是要调整的内存地址
size 调整之后新大小
返回值为调整之后的内存起始位置。
这个函数调整原内存空间大小的基础上,还会将原来内存中的数据移动到新的空间
realloc在调整内存空间的是存在两种情况:
情况1:原有空间之后有足够大的空间
情况2:原有空间之后没有足够大的空间


当是情况1 的时候,要扩展内存就直接原有内存之后直接追加空间,原来空间的数据不发生变化。
当是情况2 的时候,原有空间之后没有足够多的空间时,扩展的方法是:在堆空间上另找一个合适大小
的连续空间来使用。这样函数返回的是一个新的内存地址。
由于上述的两种情况,realloc函数的使用就要注意一些。
?

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

int main()
{
	int* p = (int*)malloc(40);
	if (p == NULL)
	{
		printf("%s\n", strerror(errno));
	}
	//使用
	int i;
	for (i = 0; i < 10; i++)
	{
		*(p + i) = i + 1;
	}
	//扩容
	int* ptr = (int*)realloc(p, 80);//扩容到80个字节 相当于原来有40 又加上40
	//不要直接用原来的指针接收 如果realloc返回NULL 可能导致数据流失
	if (ptr != NULL)
	{
		p = ptr;
	}
	for (i = 0; i < 10; i++)
	{
		printf("%d ", *(p + i));
	}
	//realloc(NULL, 40) 等价于  malloc(40);
	return 0;
}

2.常见的动态内存错误

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

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

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

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

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

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

3.柔性数组

1.柔性数组的定义方法

struct S
{
    int i;
    int a[0];//柔性数组成员
};

有些编译器可能会报错,那么可以改成下面这种:

struct S
{
    int i;
    int a[];//柔性数组成员
};

2.柔性数组的特点和使用

结构中的柔性数组成员前面必须至少一个其他成员。
sizeof 返回的这种结构大小不包括柔性数组的内存。
包含柔性数组成员的结构用malloc ()函数进行内存的动态分配,并且分配的内存应该大于结构的大
小,以适应柔性数组的预期大小。
?

使用:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>

struct S
{
	int n;
	int arr[];
};
int main()
{
	int sz = sizeof(struct S);//4 结构体内存不计算柔性数组
	printf("%d\n", sz);
	//柔性数字的使用
	struct S* ps = (struct S*)malloc(sizeof(struct S) + 40);
	if (ps == NULL)
	{
		return 1;
	}
	int i;
	for (i = 0; i < 10; i++)
	{
		*(ps->arr + i) = i;
	}
	for (i = 0; i < 10; i++)
	{
		printf("%d ", ps->arr[i]);
	}
	struct S* str = (struct S*)realloc(ps, sizeof(struct S) + 80);
	if (str != NULL)
	{
		ps = str;
	}
	//释放
	free(ps);
	ps = NULL;
	return 0;
}

3.柔性数组的优势

上面的使用也可以写成下面这种形式:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>

struct S
{
	int n;
	int* arr;
};
int main()
{
	//柔性数字的使用
	struct S* ps = (struct S*)malloc(sizeof(struct S));
	if (ps == NULL)
	{
		return 1;
	}
	ps->n = 100;
	int* p = (int*)malloc(40);
	if (p != NULL)
	{
		ps->arr = p;
	}
	int i;
	for (i = 0; i < 10; i++)
	{
		*(ps->arr + i) = i;
	}
	for (i = 0; i < 10; i++)
	{
		printf("%d ", ps->arr[i]);
	}
	struct S* str = (struct S*)realloc(ps, sizeof(struct S) + 80);
	if (str != NULL)
	{
		ps = str;
	}
	//释放
	free(ps->arr);
	ps->arr = NULL;
	free(ps);
	ps = NULL;
	return 0;
}

与下面的相比 用柔性数组的好处显而易见

首先,方便内存释放。
如果我们的代码是在一个给别人用的函数中,你在里面做了二次内存分配,并把整个结构体返回给用户。用户调用free可以释放结构体,但是用户并不知道这个结构体内的成员也需要free,所以你不能指望用户来发现这个事。所以,如果我们把结构体的内存以及其成员要的内存一次性分配好了,并返回给用户一个结构体指针,用户做一次free就可以把所有的内存也给释放掉。

第二,这样有利于访问速度。

连续的内存有益于提高访问速度,也有益于减少内存碎片。
?

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-07-20 18:34:21  更:2022-07-20 18:37:43 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 13:45:37-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码