第22课【内存操作】内存操作函数 内存操作误区 柔性数组

基本知识框架

在这里插入图片描述

课堂笔记

内存操作的意义

可以动态开辟内存空间
关于开辟内存空间,按照以往的开辟方式:
int a = 5;
char str[6] = "Hello";
有两个特点:

  • 对于变量,空间开辟的大小是确定
  • 对于数组,在声明时数组的大小必须是确定的,因为编译器需要此依据来为数组开辟内存空间

我们开辟出的空间都是要事先确定的,而实际使用中,我们会有这样的需求,有些变量或数组的大小需要在程序运行后才确定,所以内存操作的意义就在于能够开辟动态地开辟内存空间

内存操作函数

malloc
void *malloc(size_t size);

功能:在内存中申请指定size大小的空间
返回值:如果申请成功则返回该内存空间的首地址,如果申请失败,返回NULL
注意:

  • 使用malloc申请空间时要注意判断是否申请成功
  • size的值如果为0,C语言中对于这样的情况是未定义的,最终结果取决于编译器
  • malloc申请出来的内存空间,不会被初始化
  • malloc申请出来的内存空间,大小以字节计算
calloc
void *calloc(size_t num, size_t size);

功能:在内存中申请连续num块指定size大小的空间
返回值:如果申请成功则返回内存空间的首地址,如果申请失败,返回NULL
注意:

  • calloc申请出来的内存空间,会被初始化成0
  • calloc申请出来的内存空间,大小以字节计算
realloc
void *realloc(void *ptr, size_t size)

功能:重新分配(变大 / 变小)之前已经申请的内存空间
返回:如果分配成功则返回重新分配内存空间的首地址,如果分配失败,返回NULL
注意:

  • 使用realloc扩大内存空间的容量时,要保证地址后的后有足够的内存空间余量。如果当前要扩容的地址之后没有足够的余量,那么realloc会另外在有富余空间的地方进行扩容,并返回扩容空间后的地址,而不是返回你要扩容的地址
  • realloc操作保留源地址指向空间中的数据,即使是出现上一种扩容空间不足的情况,也会讲数据重新复制到新空间
  • 传入的参数包括要重新分配空间内存块的首地址ptr,以及要重新分配大小size
free
void free(void *ptr)

功能:释放已被分配内存空间
返回:无返回值
注意:

  • 指针ptr指向要释放的内存空间
  • 指针ptr不要指向非动态开辟的内存,这样的情况是未定义的
  • 当指针ptr的值为NULL时,不会有任何效果
  • 当分配的内存块较大时,并且不再使用时,要使用free函数去释放内存空间
memcpy
void *memcpy(void *dest, void *src, unsigned int count);

功能:拷贝内存空间,将源地址src指向空间拷贝count个字节的内容到目标地址dest指向的空间
返回值:返回拷贝完成后目标地址的指针dest
注意:

  • 内存拷贝的字节数由count决定
  • 当count>sizeof(src)时,会在末尾补上’\0’
  • 源地址与目标地址指向的空间不能重叠
memmove
void *memmove(void *dest, void *src, unsigned int count);

功能:移动(拷贝)内存空间
返回值:同memcpy
注意:

  • 和memcpy一样的功能,同样是拷贝内存空间
  • memmove可以处理地址指向空间重叠的问题,注意源地址src只想的空间可能会被改变
memset
void *memset(void *buffer, int c, int count);

功能:初始化内存空间,将buffer指向的内存空间中的前count 个字节的内容初始化为整形数据c
返回值:初始化后的内存空间
注意:

  • 一般用于给数组、字符串等类型赋值
memcmp
int memcmp(void *buf1, void *buf2, unsigned int count);

功能:比较buffer1和buffer2指向内存空间的前count个字节
返回值:比较结果,有符号整型数
注意:

  • 功能上其实与strncmp差不多,都是根据ASCII码进行比较
memchr
void *memchr(const void *str, int c, size_t n)

功能:查找str指向的内存空间中前n个字节,是否出现字符c
返回值:如果查找成功,返回str中出现字符c的地址。如果查找失败,返回NULL
注意:

  • 传入的字符是以int的形式传入的,查找是都是将这个值看作无符号字符

内存操作误区

以下的操作是错误的,需要注意:

  • 对NULL指针进行解引用
int* a = NULL;
*a = 6;	// 对NULL进行解引用
  • 对动态开辟的内存空间进行越界的访问
int i = 0;
int* num = NULL;
num = (int*)malloc(10*sizeof(int));
for(i=0; i<=10; i++)	// 循环到第11次时,*num访问的空间越界
{
	*num = i;
	num++;
}
  • 对非动态开辟的内存空间使用free
int a = 10;
int *pa = &a;
free(pa);	// 使用free释放非动态开辟空间
  • 对动态开辟的内存空间的一部分使用ree
int* pa = (int*)malloc(4*sizeof(int));  
pa++;
free(pa);	// 使用free释放动态开辟的部分空间
  • 堆动态开辟的内存空间多次使用free
int *pa = (int*)malloc(sizeof(int));
free(pa);	
free(pa);	// 多次使用free释放动态开辟的空间
  • 动态开辟内存空间后不使用free,有空间泄漏的隐患
int* p = NULL;
p = (int*)malloc(sizeof(int));

C/C++程序的内存分配

程序存储时的存储分配

程序在硬盘中存储时分为三部分:

  • 代码区:用于存放CPU可执行的机器指令,是只读区域,不能被修改
  • 静态数据区:用于存放被初始化的全局变量,静态变量(全局静态变量和局部静态变量),常量数据(如常量字符串)
  • BSS代码区:用于存放未初始化的全局变量。BSS区域的数据在程序执行之前会被初始化为0或者NULL

程序运行时的存储分配

程序在载入内存运行时分为四个部分:

  • 代码区:存放函数体的二进制码。对于顺序指令,只会执行一次。如有反复指令,则需要使用跳转,如有递归指令,则需使用栈
  • 数据段:
    1 . 初始化的数据段
    2. 未初始化的数据段(BSS)
    3. 只读数据段(常量数据)
    4. 已经初始化的可读写数据段
  • 栈:栈是由高地址向低地址生长的数据结构,是一块连续的内存,容量较小,用于存放函数的参数和局部变量。栈的空间由系统管理,无需程序员参与。
  • 堆:堆是由低地址向高地址生长的数据结构,是一块不连续的内存,容量较大,用于开辟动态分配的内存,比如malloc,calloc,realloc。需要程序员自行申请和释放

柔性数组

柔性数组的概念:零长度的数组,通常定义于可变长度结构体中,从而使得结构体的大小可以动态的改变

柔性数组的定义方法:

struct unit
{
	int a;
	char arr[0];
}

柔性数组的特点

  • 柔性数组不能是可变结构体中的唯一成员
  • 柔性数组必须定义在结构体的末尾
  • 可变结构体的大小,不包括柔性数组的大小

可变结构体中的柔性数组数组名本身只是个偏移量,不占用任何空间,仅仅代表了一个地址常量

柔性数组的使用

可以用作可变长度数据缓冲区,节约空间且使用方便

//  *****************
//  数据缓冲区的声明 *
//  *****************
//  可变长结构体数据缓冲区
//  长度可变,节约空间
//  只需free一次即可释放空间
struct zero_buffer
{
    int     len;
    char    data[0];
};

//  定长结构体数据缓冲区
//  长度固定,容易造成空间浪费
//  只需free一次即可释放空间
struct max_buffer
{
    int     len;
    char    data[MAX_LENGTH];
};

//  指针结构体数据缓冲区
//  长度固定,容易造成空间浪费
//  只需free两次即可释放空间
struct point_buffer
{
    int     len;
    char*   data;
};

//  *****************
//  数据缓冲区的定义 *
//  *****************
//  可变长结构体数据缓冲区
struct zero_buffer*  zbuffer = NULL;
//  定长结构体数据缓冲区
struct max_buffer*   mbuffer = NULL;
//  指针结构体数据缓冲区
struct point_buffer* pbuffer = NULL;

//  *****************
//  数据缓冲区的使用 *
//  *****************
//  可变长结构体数据缓冲区
zbuffer = (struct zero_buffer*)malloc(sizeof(struct zero_buffer)   + len*sizeof(char));
//  定长结构体数据缓冲区
mbuffer = (struct zero_buffer*)malloc(sizeof(struct max_buffer)    + MAX_LENGTH*sizeof(char));
//  指针结构体数据缓冲区
pbuffer = (struct zero_buffer*)malloc(sizeof(struct point_buffer)  + sizeof(char*));
pbuffer->data  = (char*)malloc(len);	//  辅助pbuffer开辟data的空间

//  *****************
//  数据缓冲区的销毁 *
//  *****************
//  可变长结构体数据缓冲区
free (zbuffer);
zbuffer = NULL;
//  定长结构体数据缓冲区
free (mbuffer);
mbuffer = NULL;
//  指针结构体数据缓冲区
free (pbuffer);
free (pbuffer->pdata);
pbuffer = NULL;
pbuffer->pdata = NULL;

柔性数组的优势

  • 无需提前定义数组大小
  • 使用灵活,可动态开辟空间
  • 对空间的使用效率更高

基本知识框架Xmind文件下载

链接: 资源下载

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值