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;
}
相关推荐
h汉堡2 小时前
类和对象(二)
开发语言·数据结构·c++·学习
Coding_Doggy3 小时前
java面试day5 | 消息中间件、RabbitMQ、kafka、高可用机制、死信队列、消息不丢失、重复消费
java·开发语言·面试
MoRanzhi12033 小时前
基于 SciPy 的矩阵运算与线性代数应用详解
人工智能·python·线性代数·算法·数学建模·矩阵·scipy
emplace_back3 小时前
C# 主窗口 单例 唯一进程 互斥锁
开发语言·c#
仰泳的熊猫3 小时前
LeetCode:239. 滑动窗口最大值
数据结构·c++·算法·leetcode
CoovallyAIHub3 小时前
版本号突袭!官方预览:YOLO26正式宣布,10月发布,CPU推理速度提升43%
深度学习·算法·计算机视觉
励志不掉头发的内向程序员3 小时前
【Linux系列】让 Vim “跑”起来:实现一个会动的进度条
linux·运维·服务器·开发语言·学习
学习路上_write3 小时前
新版Pycharm添加导入anaconda的python解释器
开发语言·python·pycharm
光影少年4 小时前
AI大模型开发语言排行
开发语言·人工智能