C语言第23讲

动态内存管理

复制代码
int n = 10;                 int arr[20];
char c = 'W';               char ch[20];

一个元素 一片空间,上面两种一旦申请成功,空间大小就无法调整

动态内存,是可以根据情况进行内存大小修改的

下面是4个内存函数

malloc

free

calloc

realloc

4个函数头文件都是 <stdlib.h>

4个函数所操控的空间都在堆区上

malloc

malloc的原型:

void* malloc(size_t size)

size 为你要想申请的空间大小,单位为字节

void* 这个函数向内存申请一块联系可用的空间,

成功返回这片空间的起始位置

失败返回 NULL

free

free的原型:

void free (void* ptr)

ptr为你向释放空间的起始地址

如果传参为 NULL free函数什么也不会做

专门用来做动态内存的释放和回收的,如果不释放,程序在运行时这片内存一直是你的

free(p); free释放了p指向的空间后p会成为野指针,所以要尽早赋值 NULL

p = NULL;

calloc

calloc的原型:

void * calloc(size_t num, size_t size);

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

calloc 和 malloc 的区别点就两

1.原型传参的不同

2.calloc多了给空间初始化的功能

realloc

realloc的原型:

void realloc (void* ptr, size_t size)

void* 为申请调节空间的起始地址

size 为准备申请调整成 size 大的空间,大小为字节

realloc 是动态内存的关键

realloc 在开辟空间会有3种情况:

1.调整失败返回 NULL

所以,常常在使用玩后会追加判断,以避免使用 realloc 后返回NULL

2.3.情况如图

2.为连续空间开辟 后续空间足够

3.为连续空间开辟 后续空间不足

情况3 .会在空间中再找一块足够的内存空间,将原来空间的数据拷贝一份到新的空间,然后释放旧的空间,返回新的空间起始位置

有类写法,可以将 realloc 的作用和 malloc 一样

复制代码
realloc(NULL,20);  等于   malloc(20);

给 realloc 传NULL,realloc会因为找不到地址,重新开辟一块空间

常见的动态内存错误

1.对NULL指针解引用操作

cpp 复制代码
int *p =(int*)malloc(int_MAX);
*p = 20; // 报错,p有对NULL进行解引用的风险
free(p);
p = NULL;

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

cpp 复制代码
void test()
 {
 int i = 0;
 int *p = (int *)malloc(10*sizeof(int));
 if(NULL == p)
 {
 exit(EXIT_FAILURE);
 }
 for(i=0; i<=10; i++)
 {
 }
 free(p);
 }

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

cpp 复制代码
int a = 10;
int* p = &a;
free(p);
P = NULL;

4.使用free释放动态内存空间的一部分

cpp 复制代码
 int *p = (int *)malloc(100);
 p++;
 free(p) // P 已经不是内存的起始位置了

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

cpp 复制代码
 int *p = (int *)malloc(100);
 free(p);
 free(p);// 重复释放

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

当程序结束时,内存会由操作系统回收,但有程序7 * 24小时运行(游戏)

cpp 复制代码
void test()
 {
    int *p = (int *)malloc(100);
     if(NULL != p)
     {
         *p = 20;
     }
 }

 int main()
 {
     test();
     while(1);
 }  

问题1:下方代码哪里有问题

cpp 复制代码
void GetMemory(char *p)       // 2.p 为新创建的指针变量 内存中存着 NULL
 {
 p = (char *)malloc(100);     // 3.开辟的内存没有传递过去,也没有释放,导致内存泄漏
 }
 void Test(void)
 {
 char *str = NULL;
 GetMemory(str);              // 1.指针变量 本质还是变量,所以这是传值调用
 strcpy(str, "hello world");  // 4.str 为空指针,对空指针进行解引用导致程序崩溃
 printf(str);
 }

**问题2:**下方代码哪里有问题

cpp 复制代码
char *GetMemory(void)
 {
 char p[] = "hello world";
 return p;
 }
 void Test(void)
 {
 char *str = NULL;
 str = GetMemory();          // 成功返回数组p的地址,但是指针指向的空间已被程序回收 
 printf(str);                // str为野指针 回收的空间内容可能会被覆盖,可能未被覆盖
 }                    

柔性数组:

结构体中的最后一个元素是未知大小的数组,就叫做柔性数组

达成条件:

1.结构体中最后一个数组

2.数组未指定大小

柔性数组的两种写法:

cpp 复制代码
// 1
struct S
{
    int a;
    int arr[];
};

// 2
struct S
{
    int a;
    int arr[0];
}

特点:

1.结构体中柔性数组成员前必须至少有一个其他成员

2.sizeof 在计算结构体内存大小时不会包括柔性数组

3.包含柔性数组成员的结构用malloc函数进行的动态分配,并且分配的内存大小应该大于结构的大小,以适应柔性数组的大小

柔性数组的使用:

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS 
#include <stdio.h>
#include <stdlib.h>

struct S
{
	int a;
	int arr[];
} * p;

int main()
{
	struct S* ptr = (struct S*)malloc(sizeof(struct S) + 10 * sizeof(int));
	if (ptr == NULL)
	{
		perror("ptr");
		return 1;
	}
	p = ptr;
	ptr = NULL;

	for (int i = 0; i < 10; i++)
		*(p->arr + i) = i;

	for (int i = 0; i < 10; i++)
		printf("%d ", *(p->arr+ i));

	free(p);
	p = NULL;
	return 0;
}
相关推荐
Han.miracle1 小时前
数据结构——二叉树的从前序与中序遍历序列构造二叉树
java·数据结构·学习·算法·leetcode
北冥湖畔的燕雀2 小时前
C++泛型编程(函数模板以及类模板)
开发语言·c++
mit6.8243 小时前
前后缀分解
算法
QX_hao3 小时前
【Go】--map和struct数据类型
开发语言·后端·golang
你好,我叫C小白3 小时前
C语言 循环结构(1)
c语言·开发语言·算法·while·do...while
Evand J5 小时前
【MATLAB例程】基于USBL和DVL的线性回归误差补偿,对USBL和DVL导航数据进行相互补偿,提高定位精度,附代码下载链接
开发语言·matlab·线性回归·水下定位·usbl·dvl
朱嘉鼎5 小时前
状态机的介绍
c语言·单片机
寂静山林6 小时前
UVa 10228 A Star not a Tree?
算法
爱喝白开水a6 小时前
LangChain 基础系列之 Prompt 工程详解:从设计原理到实战模板_langchain prompt
开发语言·数据库·人工智能·python·langchain·prompt·知识图谱
Neverfadeaway6 小时前
【C语言】深入理解函数指针数组应用(4)
c语言·开发语言·算法·回调函数·转移表·c语言实现计算器