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;
}
相关推荐
sunywz2 分钟前
【JVM】(4)JVM对象创建与内存分配机制深度剖析
开发语言·jvm·python
亲爱的非洲野猪2 分钟前
从ReentrantLock到AQS:深入解析Java并发锁的实现哲学
java·开发语言
星火开发设计3 分钟前
C++ set 全面解析与实战指南
开发语言·c++·学习·青少年编程·编程·set·知识
李泽辉_14 分钟前
深度学习算法学习(一):梯度下降法和最简单的深度学习核心原理代码
深度学习·学习·算法
꧁Q༒ོγ꧂17 分钟前
算法详解---大纲
算法
沛沛老爹18 分钟前
Web开发者进阶AI:Agent Skills-深度迭代处理架构——从递归函数到智能决策引擎
java·开发语言·人工智能·科技·架构·企业开发·发展趋势
m0_6038887123 分钟前
Scaling Trends for Multi-Hop Contextual Reasoning in Mid-Scale Language Models
人工智能·算法·ai·语言模型·论文速览
Good_Starry26 分钟前
Java——正则表达式
java·开发语言·正则表达式
Xの哲學26 分钟前
Linux io_uring 深度剖析: 重新定义高性能I/O的架构革命
linux·服务器·网络·算法·边缘计算
二哈喇子!31 分钟前
前端HTML、CSS、JS、VUE 汇总
开发语言·前端