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

学习数据结构重要的三个部分:指针、结构体、动态内存管理(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.pswp.cn/pingmian/83063.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C# 密封类和密封方法

密封(sealed)是C#中用于限制继承和多态行为的关键字&#xff0c;它可以应用于类和方法&#xff0c;提供了一种控制继承层次的方式。 密封类 特点 使用 sealed 关键字修饰的类密封类不能被其他类继承&#xff0c;但可以继承其他类或接口主要用于防止派生所有结构(struct)都是…

thinkpad T-440p 2025.05.31

thinkpad T-440p 2025.05.31 老了退休了&#xff0c;说起来真的可恶现在笔记本的设计师&#xff0c;只有固态硬盘了

WPS自动换行

换行前 换行后 快捷键 第一步&#xff1a;启用「自动换行」功能 选中目标单元格/区域&#xff1a;点击需要设置的单元格&#xff08;或拖动选中多个单元格&#xff09;。开启自动换行&#xff08;3种方式任选&#xff09;&#xff1a; 快捷按钮&#xff1a;在顶部菜单栏点击「…

cuda_fp8.h错误

现象&#xff1a; cuda_fp8.h错误 原因&#xff1a; CUDA Toolkit 小于11.8,会报fp8错误&#xff0c;因此是cuda工具版本太低。通过nvcc --version查看 CUDA Toolkit 是 NVIDIA 提供的一套 用于开发、优化和运行基于 CUDA 的 GPU 加速应用程序的工具集合。它的核心作用是让开发…

【TTS】基于GRPO的流匹配文本到语音改进:F5R-TTS

论文地址&#xff1a;https://arxiv.org/abs/2504.02407v3 摘要 我们提出了F5R-TTS&#xff0c;这是一种新颖的文本到语音(TTS)系统&#xff0c;它将群体相对策略优化(GRPO)集成到基于流匹配的架构中。 通过将流匹配TTS的确定性输出重新表述为概率高斯分布&#xff0c;我们的方…

头歌java课程实验(Java面向对象 - 包装类)

第1关&#xff1a;基本数据类型和包装类之间的转换 任务描述 本关任务&#xff1a;实现基本数据类型与包装类之间的互相转换。 相关知识 为了完成本关任务&#xff0c;你需要掌握&#xff1a; 1.什么是包装类&#xff1b; 2.怎么使用包装类。 什么是包装类 在JAVA中&#x…

实现一个免费可用的文生图的MCP Server

概述 文生图模型为使用 Cloudflare Worker AI 部署 Flux 模型&#xff0c;是参照视频https://www.bilibili.com/video/BV1UbkcYcE24/?spm_id_from333.337.search-card.all.click&vd_source9ca2da6b1848bc903db417c336f9cb6b的复现Cursor MCP Server实现是参照文章https:/…

ES6 深克隆与浅克隆详解:原理、实现与应用场景

ES6 深克隆与浅克隆详解&#xff1a;原理、实现与应用场景 一、克隆的本质与必要性 在 JavaScript 中&#xff0c;数据分为两大类型&#xff1a; 基本类型&#xff1a;Number、String、Boolean、null、undefined、Symbol、BigInt引用类型&#xff1a;Object、Array、Functio…

新闻数据加载(鸿蒙App开发实战)

本案例基于ArkTS的声明式开发范式&#xff0c;介绍了数据请求和onTouch事件的使用。包含以下功能&#xff1a; 数据请求。列表下拉刷新。列表上拉加载。 网络数据请求需要权限&#xff1a;ohos.permission.INTERNET 一、案例效果截图 操作说明&#xff1a; 点击应用进入主页…

办公效率王Word批量转PDF 50 +文档一键转换保留原格式零错乱

各位办公小能手们&#xff0c;我跟你们说啊&#xff01;在办公的时候&#xff0c;咱经常会碰到要把一堆Word文档转成PDF格式的情况&#xff0c;比如说要统一文件格式、保护文档内容或者方便分享啥的。这时候&#xff0c;就需要用到Word批量转换成PDF的软件啦。下面我就给你们好…

一张Billing项目的流程图

流程图 工作记录 2016-11-11 序号 工作 相关人员 1 修改Payment Posted的导出。 Claim List的页面加了导出。 Historical Job 加了Applied的显示和详细。 郝 识别引擎监控 Ps (iCDA LOG :剔除了160篇ASG_BLANK之后的结果): LOG_File 20161110.txt BLANK_CDA/ALL 45/10…

SpringAI系列4: Tool Calling 工具调用 【感觉这版本有bug】

前言&#xff1a;在最近发布的 Spring AI 1.0.0.M6 版本中&#xff0c;其中一个重大变化是 Function Calling 被废弃&#xff0c;被 Tool Calling 取代。Tool Calling工具调用&#xff08;也称为函数调用&#xff09;是AI应用中的常见模式&#xff0c;允许模型通过一组API或工具…

第六十三节:深度学习-模型推理与后处理

深度学习模型训练完成后,如何高效地将其部署到实际应用中并进行准确预测?这正是模型推理与后处理的核心任务。OpenCV 的 dnn 模块为此提供了强大支持,本文将深入探讨 OpenCV 在深度学习模型推理与后处理中的关键技术与实践。 第一部分:基础概念与环境搭建 1.1 核心概念解析…

uniapp开发企业微信小程序时 wx.qy.login 在uniapp中使用的时候,需要导包吗?

在 UniApp 中使用 “wx.qy.login” 不需要手动导包&#xff0c;但需要满足以下条件&#xff1a; 一、环境要求与配置 1&#xfffd; 企业微信环境判断 必须确保当前运行环境是企业微信客户端&#xff0c;通过 “uni.getSystemInfoSync().environment” 判断是否为 “wxwork”…

ONLYOFFICE文档API:更强的安全功能

在数字化办公时代&#xff0c;文档的安全性与隐私保护已成为企业和个人用户的核心关切。如何确保信息在存储、传输及协作过程中的安全&#xff0c;是开发者与IT管理者亟需解决的问题。ONLYOFFICE作为一款功能强大的开源办公套件&#xff0c;不仅提供了高效的文档编辑与协作体验…

Linux系统编程之共享内存

概述 在Linux系统中&#xff0c;共享内存也是一种高效的进程间通信机制&#xff0c;允许两个或多个进程共享同一块物理内存区域。通过这种方式&#xff0c;不同进程可以直接访问和操作相同的数据&#xff0c;从而避免了数据的复制。由于数据直接在内存中共享&#xff0c;没有额…

零知开源——STM32F407VET6驱动Flappy Bird游戏教程

简介 本教程使用STM32F407VET6零知增强板驱动3.5寸TFT触摸屏实现经典Flappy Bird游戏。通过触摸屏控制小鸟跳跃&#xff0c;躲避障碍物柱体&#xff0c;挑战最高分。项目涉及STM32底层驱动、图形库移植、触摸控制和游戏逻辑设计。 目录 简介 一、硬件准备 二、软件架构 三、…

Elasticsearch创建快照仓库报错处理

创建快照仓库报错&#xff1a; 根据报错提示的信息&#xff0c;问题可能出在 Elasticsearch 的配置中。当你尝试创建一个文件系统&#xff08;fs&#xff09;类型的快照仓库时&#xff0c;虽然已经指定了 location 参数&#xff0c;但 Elasticsearch 仍然报错&#xff0c;这通…

服务器如何配置防火墙管理端口访问?

配置服务器防火墙来管理端口访问&#xff0c;是保障云服务器安全的核心步骤。下面我将根据你使用的不同操作系统&#xff08;Linux: Ubuntu/Debian/CentOS&#xff1b;Windows Server&#xff09;介绍常用防火墙配置方法。 ✅ 一、Linux 防火墙配置&#xff08;UFW / firewalld…

Redis最佳实践——安全与稳定性保障之连接池管理详解

Redis 在电商应用的连接池管理全面详解 一、连接池核心原理与架构 1. 连接池工作模型 #mermaid-svg-G7I3ukCljlJZAXaA {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-G7I3ukCljlJZAXaA .error-icon{fill:#552222;}…