C语言进阶--动态内存管理

article/2025/6/22 10:25:08

学习数据结构重要的三个部分:指针、结构体、动态内存管理(malloc、calloc、realloc、free)。

1.为什么存在动态内存分配?

1.空间开辟大小是固定的;

2.数组在声明时,必须指定数组的长度,它所需要的内存在编译时分配。

int main()
{int a = 10; //4个字节int arr[10]; //40个字节return 0;
}

2.动态内存函数的介绍

2.1malloc和free

C语言提供了一个动态内存开辟的函数:malloc()

这个函数向内存申请一块连续可用的空间,并返回指向这块空间的指针。

  • 如果开辟成功,则返回一个指向开辟好的空间的指针;
  • 如果开辟失败,则返回一个NULL指针。因此malloc的返回值一定要做检查;
  • 返回值的类型是void*,所以malloc函数并不知道开辟空间的类型,具体在使用的时候使用者自己来决定。
  • 如果参数size为0,malloc的行为是标准未定义的,取决于编译器。

在这里插入图片描述

#include <stdlib.h>
#include <string.h>
#include <errno.h>int main()
{int arr[10] = {0};//动态内存开辟int* p = (int*)malloc(40);if (p == NULL){printf("%s\n", strerror(errno));return 1;}//使用int i = 0;for (i = 0; i < 10; i++){*(p+i) = i;}//打印for (i = 0; i < 10; i++){printf("%d ", *(p+i)); //0 1 2 3 4 5 6 7 8 9 }return 0;
}

在这里插入图片描述

#include <stdlib.h>
#include <string.h>
#include <errno.h>int main()
{int arr[10] = {0};//动态内存开辟int* p = (int*)malloc(INT_MAX);if (p == NULL){printf("%s\n", strerror(errno));return 1;}//使用int i = 0;for (i = 0; i < 10; i++){*(p+i) = i;}//打印for (i = 0; i < 10; i++){printf("%d ", *(p+i)); //0 1 2 3 4 5 6 7 8 9 }return 0;
}
free(动态内存的释放和回收)
void free(void* ptr)
  • 如果参数ptr指向的空间不是动态开辟的,那么free函数的行为是未定义的;
  • 如果参数ptr是NULL指针,则函数什么事情也不做。

没有free,并不是说内存空间就不回收了。当程序退出时,系统会自动回收内存空间。

#include <stdlib.h>
#include <string.h>
#include <errno.h>int main()
{int arr[10] = {0};//动态内存开辟int* p = (int*)malloc(40);if (p == NULL){printf("%s\n", strerror(errno));return 1;}//使用int i = 0;for (i = 0; i < 10; i++){*(p+i) = i;}//打印for (i = 0; i < 10; i++){printf("%d ", *(p+i)); //0 1 2 3 4 5 6 7 8 9 }free(p);p = NULL; //最好的做法,free释放后,赋值为NULLreturn 0;
}

在这里插入图片描述

//error,必须释放动态开辟的空间
int main()
{int a = 10;int* p = &a;//···free(p); p = NULL;return 0;
}
int main()
{int* p = NULL;free(p); //啥事也不干return 0;
}

2.2calloc

在这里插入图片描述

#include <stdlib.h>
#include <string.h>
#include <errno.h>//开辟10个整型的空间
int main()
{int* p = (int*)calloc(10, sizeof(int));if (p == NULL){printf("%s\n", strerror(errno));return 1;}//打印int i = 0;for (i = 0; i < 10; i++){printf("%d ", *(p+i)); //calloc会初始化为全0}//释放free(p);p = NULL;return 0;
}

可以理解为:calloc=malloc+memset

2.3realloc

在这里插入图片描述
在这里插入图片描述

#include <stdlib.h>
#include <string.h>
#include <errno.h>int main()
{int* p = (int*)malloc(40);if (NULL == p){printf("%s\n", strerror(errno));return 1;}//使用1 2 3 4 5 6 7 8 9 10int i = 0;for (i = 0; i < 10; i++){*(p+i) = i+1;}//扩容int* ptr = (int*)realloc(p, 80);if (ptr != NULL)  //扩容成功{p = ptr;}//打印for (i = 0; i < 10; i++){printf("%s\n", *(p+i)); //1 2 3 4 5 6 7 8 9 10}//释放free(p);p = NULL;return 0;
}

第一种情况:
在这里插入图片描述
在这里插入图片描述
第二种情况:
在这里插入图片描述

int main()
{realloc(NULL, 40); //等价于malloc(40);return 0;
}

练习:动态版本的通讯录

动态版本的通讯录:
1.通讯录默认能存放3个人的信息
2.如果空间不够了,就增加空间,每次增加2个人的空间

//test.c
#define _CRT_SECURE_NO_WARNINGS #include "contact.h"void menu()
{printf("*************************************************\n");printf("************1.add     2.del**********************\n");printf("************3.search  4.modify*******************\n");printf("************5.show    6.sort*********************\n");printf("************     0.exit    **********************\n");printf("*************************************************\n");
}
int main()
{int input = 0;Contact con; //通讯录InitContact(&con); //初始化通讯录do{menu();printf("请选择:>");scanf("%d", &input);switch (input){case 1:AddContact(&con);break;case 2:DelContact(&con);break;case 3:SearchContact(&con);break;case 4:ModifyContact(&con);break;case 5:ShowContact(&con);break;case 6:SortContact(&con);break;case 0:DestroyContact(&con); //动态版本,销毁通讯录printf("退出通讯录\n");break;default:printf("选择错误\n");break;}} while (input);return 0;
}
//contact.h
#pragma once#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <stdlib.h>#define DEFAULT_SZ 3
#define INC_SZ 2#define MAX 100
#define MAX_NAME 20
#define MAX_SEX 10
#define MAX_TELE 12
#define MAX_ADDR 30//类型的声明
typedef struct PeoInfo  //人的信息
{char name[MAX_NAME];int age;char sex[MAX_SEX];char tele[MAX_TELE];char addr[MAX_ADDR];
}PeoInfo;//静态版本
//typedef struct Contact //通讯录的信息
//{
//	PeoInfo data[MAX]; //存放人的信息
//	int count; //记录当前通讯录中实际人的个数
//}Contact;//动态版本
typedef struct Contact //通讯录的信息
{PeoInfo* data; //存放人的信息int count; //记录当前通讯录中实际人的个数int capacity;//记录当前通讯录的容量
}Contact;int InitContact(Contact* pc); //初始化通讯录void DestroyContact(Contact* pc); //动态版本,销毁通讯录void AddContact(Contact* pc); //添加联系人的通讯录void ShowContact(const Contact* pc); //打印通讯录的信息void DelContact(Contact* pc); //删除指定联系人void SearchContact(Contact* pc); //查找指定联系人void ModifyContact(Contact* pc); //修改指定联系人void SortContact(Contact* pc); //排序通讯录中的内容,可以按照名字来排序,按照年龄来排序···
//contact.c
#define _CRT_SECURE_NO_WARNINGS #include "contact.h"//静态版本
//void InitContact(Contact* pc)
//{
//	assert(pc);
//	pc->count = 0;
//	memset(pc->data, 0, sizeof(pc->data));
//}//动态版本
int InitContact(Contact* pc)
{assert(pc);pc->count = 0;pc->data = (PeoInfo*)calloc(3, sizeof(PeoInfo));if (pc->data == NULL){printf("InitContact::%s\n", strerror(errno));return 1;}pc->capacity = DEFAULT_SZ;return 0;
}void DestroyContact(Contact* pc) //动态版本,销毁通讯录
{assert(pc);free(pc->data);pc->data = NULL;
}//静态版本
//void AddContact(Contact* pc)
//{
//	assert(pc);
//	if (pc->count == MAX)
//	{
//		printf("通讯录已满,无法添加\n");
//		return;
//	}
//	printf("请输入名字:>");
//	scanf("%s", pc->data[pc->count].name);
//	printf("请输入年龄:>");
//	scanf("%d", &(pc->data[pc->count].age));
//	printf("请输入性别:>");
//	scanf("%s", pc->data[pc->count].sex);
//	printf("请输入电话:>");
//	scanf("%s", pc->data[pc->count].tele);
//	printf("请输入地址:>");
//	scanf("%s", pc->data[pc->count].addr);
//
//	pc->count++;
//	printf("添加成功\n");
//}//动态版本
void CheckCapacity(Contact* pc)
{if (pc->count == pc->capacity){PeoInfo* ptr = (PeoInfo*)realloc(pc->data, (pc->capacity + INC_SZ)*sizeof(PeoInfo));if (ptr == NULL){printf("AddContact::%s\n", strerror(errno));return;}else{pc->data = ptr;pc->capacity += INC_SZ;printf("扩容成功\n");}}
}
void AddContact(Contact* pc)
{assert(pc);//扩容CheckCapacity(pc);printf("请输入名字:>");scanf("%s", pc->data[pc->count].name);printf("请输入年龄:>");scanf("%d", &(pc->data[pc->count].age));printf("请输入性别:>");scanf("%s", pc->data[pc->count].sex);printf("请输入电话:>");scanf("%s", pc->data[pc->count].tele);printf("请输入地址:>");scanf("%s", pc->data[pc->count].addr);pc->count++;printf("添加成功\n");
}void ShowContact(const Contact* pc)
{assert(pc);int i = 0;printf("%-20s\t%-5s\t%-5s\t%-12s\t%-30s\n", "名字", "年龄", "性别", "电话", "地址");for (i = 0; i < pc->count; i++){printf("%-20s\t%-5d\t%-5s\t%-12s\t%-30s\n", pc->data[i].name,pc->data[i].age,pc->data[i].sex,pc->data[i].tele,pc->data[i].addr);}
}static int FindByName(Contact* pc, char name[])
{assert(pc);int i = 0;for (i = 0; i < pc->count; i++){if (0 == strcmp(pc->data[i].name, name)){return i;}}return -1;
}void DelContact(Contact* pc)
{char name[MAX_NAME] = { 0 };assert(pc);int i = 0;if (pc->count == 0){printf("通讯录为空,没有信息可以删除\n");return;}printf("请输入要删除人的名字:>");scanf("%s", name);//删除//1.查找int pos = FindByName(pc, name);if (pos == -1){printf("要删除的人不存在\n");return;}//2.删除for (i = pos; i < pc->count - 1; i++){pc->data[i] = pc->data[i + 1];}pc->count--;printf("删除成功\n");}void SearchContact(Contact* pc)
{assert(pc);char name[MAX_NAME] = { 0 };printf("请输入要查找人的名字:>");scanf("%s", name);//1.查找int pos = FindByName(pc, name);if (pos == -1){printf("要查找的人不存在\n");return;}//2.打印printf("%-20s\t%-5s\t%-5s\t%-12s\t%-30s\n", "名字", "年龄", "性别", "电话", "地址");printf("%-20s\t%-5d\t%-5s\t%-12s\t%-30s\n", pc->data[pos].name,pc->data[pos].age,pc->data[pos].sex,pc->data[pos].tele,pc->data[pos].addr);}void ModifyContact(Contact* pc)
{assert(pc);char name[MAX_NAME] = { 0 };printf("请输入要修改人的名字:>");scanf("%s", name);//1.查找int pos = FindByName(pc, name);if (pos == -1){printf("要修改的人不存在\n");return;}printf("要修改的人的信息已经找到,接下来开始修改\n");//2.修改printf("请输入名字:>");scanf("%s", pc->data[pos].name);printf("请输入年龄:>");scanf("%d", &(pc->data[pos].age));printf("请输入性别:>");scanf("%s", pc->data[pos].sex);printf("请输入电话:>");scanf("%s", pc->data[pos].tele);printf("请输入地址:>");scanf("%s", pc->data[pos].addr);printf("修改成功\n");
}int cmp_peo_by_name(const void* e1, const void* e2)
{return strcmp(((PeoInfo*)e1)->name, ((PeoInfo*)e2)->name);
}
void SortContact(Contact* pc) //按照名字来排序
{assert(pc);qsort(pc->data, pc->count, sizeof(PeoInfo), cmp_peo_by_name);printf("排序成功\n");
}

3.常见的动态内存错误

3.1对NULL指针的解引用操作

#include <stdlib.h>
int main()
{int* p = (int*)malloc(40);*p = 20; //如果p的值是NULL,就会出现问题return 0;
}

修正:

#include <stdlib.h>int main()
{int* p = (int*)malloc(40);if (p == NULL){return 1;}*p = 20;free(p);p = NULL;return 0;
}

3.2对动态开辟空间的越界访问

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>int main()
{int* p = (int*)malloc(40);if (p == NULL){printf("%s\n", strerror(errno));return 1;}//使用int i = 0;for (i = 0; i <= 10; i++)  {p[i] = i;}free(p);p = NULL;return 0;
}

3.3对非动态开辟内存使用free释放

#include <stdlib.h>int main()
{int a = 10;int* p = &a;//···free(p);p = NULL;return 0;
}

在这里插入图片描述

3.4使用free释放一块动态开辟内存的一部分

#include <stdlib.h>int main()
{int* p = (int*)malloc(40);if (p == NULL){return 1;}//使用int i = 0;for (i = 0; i < 5; i++){*p = i;p++;}//释放free(p); //此时p的地址早已不是起始位置,必须指向开辟空间的起始位置p = NULL;return 0;
}

在这里插入图片描述

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

#include <stdlib.h>int main()
{int* p = (int*)malloc(40);//···free(p);//···free(p);return 0;
}

在这里插入图片描述
修正:

#include <stdlib.h>int main()
{int* p = (int*)malloc(40);//···free(p);p = NULL;//···free(p);return 0;
}

3.6动态开辟内存忘记释放(内存泄漏)

#include <stdlib.h>void test()
{int* p = (int*)malloc(100);//···int flag = 0;scanf("%d", &flag); //5if (flag == 5)return;free(p);p = NULL;
}int main()
{test();//···return 0;
}

场景2:

#include <stdlib.h>int* test()
{int* p = (int*)malloc(100);  //开辟空间if (p == NULL){return p;}//···return p;
}int main()
{int* ret = test();//忘记释放了return 0;
}

4.经典笔试题(出自《高质量的C-C++编程》)

在这里插入图片描述
修改版本2:(有点别扭)

在这里插入图片描述

#include <stdio.h>
int main()
{printf("hello world\n"); //hello worldchar* p = "hello world";printf(p); //hello worldprintf("%s\n", p); //hello worldreturn 0;
}//要理解底层原理,传递的是h字符的地址

返回栈空间地址的问题。

#include <stdio.h>int* test()
{//返回栈空间地址的问题int a = 10;return &a;
}int main()
{int* p = test();printf("%d\n", *p); //10return 0;
}
#include <stdio.h>int* test()
{int a = 10;return &a;
}int main()
{int* p = test();printf("hehe\n"); //heheprintf("%d\n", *p); //5return 0;
}

5.C/C++程序的内存开辟

自行学习。
书籍《深入理解计算机系统》

6.柔性数组(flexible array)

C99中,结构中的最后一个元素允许是未知大小的数组,这就叫做柔性数组成员。

typedef struct st_type
{int i;int a[0]; //柔性数组成员
}type_a;

有些编译器会报错无法编译可以写成:

typedef struct st_type
{int i;int a[]; //柔性数组成员
}type_a;

6.1柔性数组的特点

结构中的柔性数组成员前面必须至少一个其它成员;

sizeof返回的这种结构大小不包括柔性数组的内存;

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

#include <stdio.h>struct S
{int n;int arr[]; //柔性数组成员
};int main()
{int sz = sizeof(struct S);printf("%d\n", sz); //4      return 0;
}

6.2柔性数组的使用

#include <stdio.h>
#include <stdlib.h>struct S
{int n;int arr[]; //柔性数组成员
};int main()
{//柔性数组的使用struct S* ps = (struct S*)malloc(sizeof(struct S) + 40);if (ps == NULL){//···return 1;}ps->n = 100;int i = 0;for (i = 0; i < 10; i++){ps->arr[i] = i;}//打印for (i = 0; i < 10; i++){printf("%d ", ps->arr[i]);}//调整struct S* ptr = (struct S*)realloc(ps, sizeof(struct S) + 80);if (ptr != NULL){ps = ptr;ptr = NULL;}//···//释放free(ps);ps = NULL;return 0;
}

方案二:

#include <stdio.h>
#include <stdlib.h>
struct S
{int n;int* arr;
};int main()
{struct S* ps =(struct S*)malloc(sizeof(struct S));if (ps == NULL){return 1;}ps->n = 100;ps->arr = (int*)malloc(40);if (ps->arr == NULL){return 1;}//使用int i = 0;for (i = 0; i < 10; i++){ps->arr[i] = i;}//打印for (i = 0; i < 10; i++){printf("%d ", ps->arr[i]);}//调整int* ptr = (int*)realloc(ps->arr, 80);if (ptr == NULL){return 1;}//释放free(ps->arr);free(ps);ps = NULL;return 0;
}

方案二:释放时容易忘记导致内存泄漏;多次开辟空间,碎片化严重,导致内存利用率下降。

6.3柔性数组的优势

方便内存释放;

理论上有利于访问速度(连续的内存有利于访问速度,减少内存碎片)。

总结

今天就暂且更新至此吧,期待下周再会。如有错误还请不吝赐教。希望对您学习有所帮助,翻页前留下你的支持,以防下次失踪了嗷。

作者更新不易,免费关注别手软。


http://www.hkcw.cn/article/iBPnXwHuZl.shtml

相关文章

Excel如何去除公式保留数值

我们有时候使用Excel在修改一部分数值的时候会导致和该数值相关的通过公式进行计算的数值发生变化&#xff0c;但有时我们不想改变这些数值&#xff0c;同样的有时我们在移动一些数值的时候会导致通过这些数值计算的数值变为#!VALUE&#xff0c;这是我们不想发生的&#xff0c;…

C++学习-入门到精通【11】输入/输出流的深入剖析

C学习-入门到精通【11】输入/输出流的深入剖析 目录 C学习-入门到精通【11】输入/输出流的深入剖析一、流1.传统流和标准流2.iostream库的头文件3.输入/输出流的类的对象 二、输出流1.char* 变量的输出2.使用成员函数put进行字符输出 三、输入流1.get和getline成员函数2.istrea…

一周学会Pandas2之Python数据处理与分析-数据重塑与透视-melt() - 融化 / 逆透视 (宽 -> 长)

锋哥原创的Pandas2 Python数据处理与分析 视频教程&#xff1a; 2025版 Pandas2 Python数据处理与分析 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili melt() 是 pandas 中用于数据重塑的核心方法之一&#xff0c;它可以将 宽格式数据 转换为 长格式数据&#xff0c;特…

设计模式——工厂方法模式(创建型)

摘要 工厂方法模式是一种创建型设计模式&#xff0c;通过定义创建对象的接口&#xff0c;让子类决定实例化哪个类。它包含抽象产品、具体产品、抽象工厂和具体工厂等角色。该模式使类的实例化延迟到子类&#xff0c;具有良好的扩展性和灵活性&#xff0c;适用于多种场景&#…

软件性能之CPU

性能是个宏大而驳杂话题&#xff0c;从代码&#xff0c;到网络&#xff0c;到实施&#xff0c;方方面面都会涉及到性能问题&#xff0c;网上对性能讲解的文章多如牛毛&#xff0c;从原理到方法再到工具都有详细的介绍&#xff0c;本文虽不能免俗&#xff0c;但期望能从另外一个…

腾讯云推出云开发AI Toolkit,国内首个面向智能编程的后端服务

5月28日&#xff0c;腾讯云开发 CloudBase 宣布推出 AI Toolkit&#xff08;CloudBase AI Toolkit&#xff09;&#xff0c;这是国内首个面向智能编程的后端服务&#xff0c;适配 Cursor 等主流 AI 编程工具。 云开发 AI Toolkit旨在解决 AI 辅助编程的“最后一公里”问题&…

当前用户的Git本地配置情况:git config --local --list

通过config命令可以查询当前用户的本地配置情况。这些配置项定义了 Git 在当前仓库中的行为&#xff0c;包括文件权限处理、符号链接处理以及大小写敏感性等。 git config --local --list core.repositoryformatversion0 指定 Git 仓库的格式版本。版本 0 是最初的格式。 cor…

修改 vscode 左侧导航栏的文字大小 (更新版)

1. 起因&#xff0c; 目的: 问题&#xff1a; vscode 左侧的文字太小了&#xff01;&#xff01;&#xff01;我最火的一篇文章&#xff0c;写的就是这个问题。 看来这个问题&#xff0c;是很广泛的一个痛点。我最近更新了 vscode&#xff0c; 这个问题又出现了。再来搞一下。…

Python训练第四十天

DAY 40 训练和测试的规范写法 知识点回顾&#xff1a; 彩色和灰度图片测试和训练的规范写法&#xff1a;封装在函数中展平操作&#xff1a;除第一个维度batchsize外全部展平dropout操作&#xff1a;训练阶段随机丢弃神经元&#xff0c;测试阶段eval模式关闭dropout 昨天我们介绍…

Fine Pruned Tiled Light Lists(精细删减的分块光照列表)

概括 在这篇文章&#xff0c; 我将介绍一种Tiled Light 变体&#xff0c;主要针对AMD Graphics Core Next&#xff08;GCN&#xff09;架构进行优化&#xff0c;我们的方法应用于游戏 古墓丽影:崛起 中&#xff0c;特别是我们在通过光列表生成和阴影贴图渲染之间交错进行异步计…

《信号与系统》第 5 章 离散时间傅里叶变换

5.0 引言 第4章研究了连续时间傅里叶变换&#xff0c;并研究了这种变换的许多特性&#xff0c;这些特性使傅里叶分析方法在分析和理解连续时间信号与系统的性质时具有很大的价值。这一章将介绍并研究离散时间傅里叶变换&#xff0c;这样就完整地建立了傅里叶分析方法。 在第3…

5.2 初识Spark Streaming

在本节实战中&#xff0c;我们初步探索了Spark Streaming&#xff0c;它是Spark的流式数据处理子框架&#xff0c;具备高吞吐量、可伸缩性和强容错能力。我们了解了Spark Streaming的基本概念和运行原理&#xff0c;并通过两个案例演示了如何利用Spark Streaming实现词频统计。…

Kafka消息中间件

window中的安装 ①、下载并解压kafka压缩包&#xff0c;进入config目录下修改zookeeper.properties配置文件 因为kafka内置了zookeeper&#xff0c;所以不需安装zookeeper。设置zookeeper数据存储位置&#xff0c;如果该路径不存在&#xff0c;则自动创建 dataDir E:/kafka…

4.2.4 Spark SQL 数据写入模式

在本节实战中&#xff0c;我们详细探讨了Spark SQL中数据写入的四种模式&#xff1a;ErrorIfExists、Append、Overwrite和Ignore。通过具体案例&#xff0c;我们演示了如何使用mode()方法结合SaveMode枚举类来控制数据写入行为。我们首先读取了一个JSON文件生成DataFrame&#…

day23-计算机网络-1

1. 网络简介 1.1. 网络介质 网线&#xff1a;cat5,cat5e 六类网线&#xff0c;七类网线&#xff0c;芭蕾网线光纤&#xff1a;wifi&#xff1a;无线路由器&#xff0c;ap5G 1.2. 常见网线类型 1.2.1. 双绞线&#xff08;Twisted Pair Cable&#xff09;【最常用】 按性能主…

Ubuntu下编译mininim游戏全攻略

目录 一、安装mininim 软件所依赖的库&#xff08;重点是allegro游戏引擎库&#xff09;二、编译mininim 软件三、将mininim打包给另一个Ubuntu系统使用四、安卓手机运行mininim 一、安装mininim 软件所依赖的库&#xff08;重点是allegro游戏引擎库&#xff09; 1. 用apt-get…

org.junit.runners.model.InvalidTestClassError:此类问题的解决

不知道大家是否遇见过以上这种情况&#xff0c;我也是今天被这个错误搞得很烦&#xff0c;后来通过网上查找资料终于找到了问题所在————就是简单的Test注解的错误使用 Test注解的注意情况 &#xff1a;1 权限必须是public 2 不能有参数 3 返回值类型是void 4 本类的其他的…

2025年渗透测试面试题总结-匿名[校招]渗透测试(打击黑灰产)(题目+回答)

安全领域各种资源&#xff0c;学习文档&#xff0c;以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具&#xff0c;欢迎关注。 目录 匿名[校招]渗透测试(打击黑灰产) 2. 实习时达成的目标 3. 文件包含漏洞 4. Redis未授权访问利用 5. 钓鱼…

【Hot 100】55. 跳跃游戏

目录 引言跳跃游戏我的解题 &#x1f64b;‍♂️ 作者&#xff1a;海码007&#x1f4dc; 专栏&#xff1a;算法专栏&#x1f4a5; 标题&#xff1a;【Hot 100】55. 跳跃游戏❣️ 寄语&#xff1a;书到用时方恨少&#xff0c;事非经过不知难&#xff01; 引言 跳跃游戏 &#x…

Go 语言的 GC 垃圾回收

序言 垃圾回收&#xff08;Garbage Collection&#xff0c;简称 GC&#xff09;机制 是一种自动内存管理技术&#xff0c;主要用于在程序运行时自动识别并释放不再使用的内存空间&#xff0c;防止内存泄漏和不必要的资源浪费。这篇文章让我们来看一下 Go 语言的垃圾回收机制是如…