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.前言

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就可以把所有的内存释放掉。其次,是有利于提高访问速度,因为是内存的开辟是连续的,也有益于减少内存碎片(没有被使用的一些内存)。

  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:35:26 
 
开发: 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年5日历 -2024/5/14 1:27:52-

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