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语言——动态内存分配

这一章主要介绍4个C语言中用于动态内存分配的函数:malloc、calloc、realloc和free

动态内存分配的必要性

动态内存分配主要用来替代数组长度必须在编译时确定的缺陷。
通过同台内存分配,我们可以在程序运行时根据需要申请适当大小的内存。

malloc

malloc函数原型如下
在这里插入图片描述
要点

  • 接收一个size_t型参数,表示开辟空间的字节数
  • 函数向内存(堆区)申请一块连续可用的空间,并返回一个指针。
  • 如果空间开辟成功,则返回这项这块空间的首地址,类型是void*。
  • 如果空间开辟失败,则返回NULL指针。由于NULL不能解引用,在使用malloc等内存配分函数时,要检查返回值再使用。
  • 如果参数为0,则结果是标准未定义的

关于void型指针
标准表示一个void
类型的指针可以转换为其他任何类型的指针,只要把void*指针赋值给目标类型指针就可以完成转换(可以不写强制类型转变换)

free

函数原型如下
在这里插入图片描述
要点

  • 该函数接受一个任何类型的参数(必须是malloc或calloc或realloc返回的地址),并将该空间释放(把空间还给操作系统),于内存分配函数配合使用。
  • 不可以只释放一部分空间,如果想释放掉一部分空间,可以用realloc实现。
  • 不能释放非动态内存分配的空间。
  • 如果传给free的是空指针,则free什么也不做,不会报错。
  • 不可将一块空间多次释放。
  • free之后,只是空间被释放了,指针变量的值未改变,为避免使用野指针的错误,建议使用free之后,自行把指针指向NULL

使用示例

#include<stdio.h>
#include<stdlib.h>
int main()
{
	int* p;
	p = malloc(10 * sizeof(int));
	if (p != NULL)
	{
		int i;
		for (i = 0; i < 10; i++)
		{
			p[i] = i;
		}
		for (i = 0; i < 10; i++)
		{
			printf("%d ", p[i]);
		}
	}
	free(p);
	p = NULL;

	return 0;
}

calloc

函数原型如下
在这里插入图片描述
这个函数和malloc很像

要点

  • 接受两个参数,一个size_t的num表示开辟元素的个数,另一个size_t型的size,表示每个元素的大小,以字节为单位。
  • 如果开辟成功,返回开辟空间的首地址。
  • 如果开辟不成功返回NULL。
  • 返回之前把每个字节的空间都初始化为0。

realloc

函数原型如下
在这里插入图片描述

这个函数用于修改开辟空间的大小

要点

  • 接收两个参数,一个是任意类型的指针(必须是malloc或calloc的返回值),另一个是需要开辟的空间(不是改变量)。
  • 如果开辟成功,返回开辟空间的首地址。
  • 如果开辟不成功返回NULL。
  • 开辟的空间仍然是一块连续空间。
  • 如果需要的空间比原来小,则释放后面的空间,未释放的空间保存原来的数据不变。
  • 如果需要的空间比原来大,并且原来空间后面有足够的连续可用空间,则直接把后面的空间加在原来的空间后面
  • 如果需要的空间比原来的大,但是原来空间后面没有足够的连续可用空间,则在另外一个地方开辟足够大小的空间,并将原来的数据拷贝到对应位置,原来的那块空间会被释放。
  • 空间开辟失败会返回NULL。
  • 建议用一个临时变量接收realloc的返回值,判断非NULL之后再赋值给维护空间的指针。
  • 如果第一个参数是NULL,则realloc的行为和malloc一样。

使用示例

#include<stdio.h>
#include<stdlib.h>
int main()
{
	int* p;
	p = malloc(10 * sizeof(int));
	if (p != NULL)
	{
		int i;
		for (i = 0; i < 10; i++)
		{
			p[i] = i;
		}
		for (i = 0; i < 10; i++)
		{
			printf("%d ", p[i]);
		}
		printf("\n");
	}

	//增容
	int* tmp;
	tmp = realloc(p, 20 * sizeof(int));
	if (tmp != NULL)
	{
		p = tmp;
		int i;
		for (i = 0; i < 20; i++)
		{
			p[i] = i;
		}
		for (i = 0; i < 20; i++)
		{
			printf("%d ", p[i]);
		}
	}
	free(p);
	p = NULL;

}

内存泄漏

分配内存但再使用完毕后不释放将引起内存泄漏(memory leak),当内存耗尽后会造成程序/系统崩溃。只能通过重启系统解决。
对于大型程序,由于程序需要长时间运行,如果存在内存泄漏将是很严重的问题。

柔性数组

C99引入了柔性数组,所谓柔性数组,就是结构体中最后一个成员可以是一个位置大小的数组,有以下两种写法

struct S1
{
	int a;
	int b[];
};

struct S2
{
	int a;
	int b[];
};

要点

  • 柔性数组前面必须至少含有一个其他类型的成员。
  • sizeof计算这种结构体的大小时,不包括柔性数组。
  • 使用这种结构的方法是用上面讲到的内存开辟函数开辟空间,用这种结构体指针来维护。

使用示例

#include<stdio.h>
#include<stdlib.h>
struct S1
{
	int a;
	int b[];
};

struct S2
{
	int a;
	int b[];
};

int main()
{
	struct S1* p1 = malloc(sizeof(struct S1) + 10 * sizeof(int));
	if (p1 != NULL)
	{
		int i;
		for (i = 0; i < 10; i++)
		{
			p1->b[i] = i;
		}
		for (i = 0; i < 10; i++)
		{
			printf("%d ", p1->b[i]);
		}
		printf("\n");
	}
	free(p1);
	p1 = NULL;

	struct S2* p2 = malloc(sizeof(struct S2) + 10 * sizeof(int));
	if (p2 != NULL)
	{
		int i;
		for (i = 0; i < 10; i++)
		{
			p2->b[i] = i;
		}
		for (i = 0; i < 10; i++)
		{
			printf("%d ", p2->b[i]);
		}
	}
	free(p2);
	p2 = NULL;
	return 0;
}

柔性数组的另一种实现

#include<stdio.h>
#include<stdlib.h>
struct S
{
	int a;
	int* b;
};
int main()
{
	struct S* p = malloc(sizeof(struct S));
	if (p != NULL)
	{
		p->b = malloc(10 * sizeof(int));
		if (p->b != NULL)
		{
			int i;
			for (i = 0; i < 10; i++)
			{
				p->b[i] = i;
			}
			for (i = 0; i < 10; i++)
			{
				printf("%d ", p->b[i]);
			}
			printf("\n");
		}
		free(p->b);
		p->b = NULL;
	}
	free(p);
	p = NULL;

	return 0;
}

这种方法有个很大的缺点是进行了两次内存分配,如果封装成函数的代码中使用了这种方法,则用户需要用户并不知道结构体内部还有一个需要释放的空间,如果直接将结构体指针指向空间释放,那么这个里面开辟的空间就找不到了,而那块空间还没有被释放,这就造成了内存泄漏。

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-09-27 13:54:18  更:2021-09-27 13:56:33 
 
开发: 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 21:55:41-

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