第十九讲:动态内存分配

第十九讲:动态内存分配

  • 1.为什么要有动态内存分配
  • 2.malloc和free
    • 2.1malloc
      • 2.1.1函数原型
      • 2.1.2函数使用
    • 2.2free
      • 2.2.1函数原型
      • 2.2.2函数使用
      • 2.2.3函数使用注意事项
        • 2.2.3.1注意点1
        • 2.2.3.2注意点2
        • 2.2.3.3注意点3
        • 2.2.3.4注意点4
    • 2.3malloc和free使用注意事项
      • 2.3.1内存覆盖
      • 2.3.2内存泄漏
  • 3.calloc和realloc
    • 3.1calloc函数
      • 3.1.1函数原型
      • 3.1.2函数使用
    • 3.2realloc函数
      • 3.2.1函数原型
      • 3.2.2函数使用
  • 4.常见的动态内存的错误
    • 4.1对NULL指针的解引用操作
    • 4.2对动态开辟空间的越界访问
    • 4.3对非动态开辟的空间使用free释放
    • 4.4使用free释放开辟空间的一部分
    • 4.5对同一块动态空间多次释放
    • 4.6动态开辟空间忘记释放
    • 4.7总结
  • 5.动态内存经典笔试题分析
    • 5.1题目1:
    • 5.2题目2:
    • 5.3题目3:
    • 5.4题目4:
  • 6.柔性数组
    • 6.1什么是柔性数组
    • 6.2柔性数组的特点
    • 6.3柔性数组的使用
    • 6.4柔性数组的优势
  • 7.总结C/C++程序中内存区域的划分
  • 8.结构体补充

1.为什么要有动态内存分配

我们之前开辟内存时,使用的方法为:

int main()
{
	int a = 10;
	char arr[20];

	return 0;
}

但是这种内存开辟模式存在着一些问题:
1.它所开辟的空间是固定的
2.数组开辟空间时,空间是确定的,无法在程序运行时改变数组开辟的空间
3.如果数组开辟的空间远大于所需要使用的空间,会造成严重的内存浪费
4.如果数组开辟的空间小于所需要的空间,程序可能会崩溃
C语言引出了动态内存开辟,让程序员自己开辟内存,比较灵活

2.malloc和free

它们都被包含在头文件<stdlib.h>或<malloc.h>中

2.1malloc

2.1.1函数原型

该函数是用于开辟内存块的函数,函数原型如下:

void *malloc( size_t size );

1.函数参数size:所要开辟的内存空间的字节数
2.函数返回值:是一个void*类型的指针

①如果内存开辟失败,会返回NULL指针
②如果内存开辟成功,则返回一个指向开辟好的内存块的指针
③返回值的类型为void类型,若要返回非void类型的指针,则要对返回值进行强制类型转换
④如果size为0,malloc的行为是未定义的,取决于编译器

2.1.2函数使用

//malloc函数使用
#include <stdlib.h> //头文件的引用

int main()
{
	//1.开辟空间
	int *pa = (int *)malloc(10 * sizeof(int));
	//开辟一个10个字节的空间,返回开头空间的指针,将他存储到pa中

	//2.空间的使用 
	//2.1在使用之前首先要判断pa是否为空指针
	if (pa == NULL)
	{
		perror("malloc");//如果为空指针,将错误写出
		return 1;
	}
	else
	{
		//先赋值
		for (int i = 0; i < 10; i++)
			*(pa + i) = i + 1;
		//后打印
		for (int i = 0; i < 10; i++)
			printf("%d ", pa[i]);//pa[i]和*(pa+i)等价
	}

	//3.空间使用完一定要释放空间
	free(pa);
	//4.并且要将pa置为空指针,否则pa会变成野指针
	pa = NULL;
	return 0;
}

需要注意的是,malloc函数在使用时开辟的空间是连续的

2.2free

我们看完了malloc函数,紧接着先说明free的使用,最后再讨论这两个函数在使用时的注意事项

2.2.1函数原型

该函数是释放动态内存开辟空间的函数

void free (void* ptr);

函数参数:指向动态内存分配的内存块的地址
函数返回值:函数没有返回值

2.2.2函数使用

在上述代码中已经写入了该函数的使用,所以这里不再进行阐述,不信你看:
在这里插入图片描述

2.2.3函数使用注意事项

2.2.3.1注意点1

如果ptr(函数参数)指向的内存空间不是通过动态内存开辟的,那么free函数的使用是未定义的,比如:

int main()
{
		int a = 0;
		int* pa = &a;//指向不是通过动态内存开辟的空间

		free(pa);
		pa = NULL;
		return 0;
}

程序会崩溃

2.2.3.2注意点2

释放已释放的内存

int main()
{
	int* pa = (int*)malloc(10 * sizeof(int));

	if (pa == NULL)
	{
		perror("malloc");
		return 1;
	}

	free(pa);
	free(pa);//两次释放空间
	return 0;
}

程序依然会崩溃,改进的方法如下·:

int main()
{
	int* pa = (int*)malloc(10 * sizeof(int));

	if (pa == NULL)
	{
		perror("malloc");
		return 1;
	}

	free(pa);
	pa = NULL;//改进点

	free(pa);
	return 0;
}

为什么会这样,我们来看注意点3

2.2.3.3注意点3

NULL指针的释放

如果ptr(函数参数)是NULL,那么free函数什么也不做

2.2.3.4注意点4

对动态开辟的空间进行释放,函数参数必须指向所开辟空间的首地址

int main()
{
	int* pa = (int*)malloc(10 * sizeof(int));

	if (pa == NULL)
	{
		perror("malloc");
		return 1;
	}
	else
	{
		pa++;//如果我们在使用开辟的空间时,对开辟空间的位置进行了修改,那么下面在释放的时候程序会崩溃
	}

	free(pa);
	pa = NULL;
	return 0;
}

2.3malloc和free使用注意事项

2.3.1内存覆盖

内存覆盖是指计算机程序中,一块内存空间的数据被意外地写入了另一块内存区域,导致原始数据丢失

申请了内存空间之后,必须检查是否分配成功,也就是这一步:

if (pa == NULL)
{
	perror("malloc");//如果为空指针,将错误写出
	return 1;
}

如果不对pa进行检查,在后续使用pa时可能发生内存覆盖的问题,对NULL指针进行解引用是一个未定义的行为,对NULL指针进行修改可能导致程序崩溃
而且,在释放内存后,不要使用指向该内存的指针,因为这块内存已经不属于你,最好的方法是传入NULL指针

2.3.2内存泄漏

内存泄漏是指在计算机程序中,动态分配的内存在不使用后没有被正常释放,导致这部分内存无法被程序的其他部分使用或无法被操作系统回收的现象
通俗来说,内存泄漏来源于它所描述的现象:程序在动态分配空间后,由于某些原因,失去了对这块空间的控制,导致无法释放它,可类比于现实生活中的液体泄漏

如果开辟内存后不进行释放,就是内存泄漏

3.calloc和realloc

3.1calloc函数

3.1.1函数原型

void* calloc (size_t num, size_t size);

该函数是为 num 个⼤⼩为 size 的元素开辟⼀块空间,并且把空间的每个字节初始化为0

函数参数:

1.num:指对象的数量
2.size:指每个对象的大小

函数返回值:

1.成功时,返回指向分配空间的指针
2.失败时,返回NULL

3.1.2函数使用

因为这个函数对申请的空间实现了初始化为0,所以如果我们对申请的空间要求初始化,可以使用这个函数,函数使用方法如下:

//calloc函数使用
#include <stdlib.h>

int main()
{
	int* pa = calloc(10, sizeof(int));//创建一个10个整形大小的空间

	//也要检查空指针
	if (pa == NULL)
	{
		perror("calloc");
		return 1;
	}
	else
	{
		for (int i = 0; i < 10; i++)
			printf("%d ", pa[i]);//会打印10个0,因为有初始化
	}

	//释放空间
	free(pa);
	pa = NULL;
	return 0;
}

3.2realloc函数

3.2.1函数原型

realloc是用于重新分配内存大小的函数

void* realloc (void* ptr, size_t size);

函数参数:
ptr:要调整的地址
size:调整之后的大小
函数返回值:函数返回调整之后内存的起始位置

3.2.2函数使用

函数在开辟空间时,无论是扩大或缩小,原来空间的内容是不变的,对于缩小,缩小的那一部分内容会被释放,但是缩减操作可能不会发生,这取决于编译器,对于扩张,存在着两种情况:
1.原有空间内存后有足够的空间
这样的话直接进行开辟就好了
2.原有空间后不足以进行内存开辟
这样的话会重新开辟一个所需大小的内存空间,并将原内存中的数据复制到现空间中,然后释放原内存空间

所以我们使用时需要注意!

int main()
{
	int* pa = calloc(10, sizeof(int));//创建一个10个整形大小的空间

	//也要检查空指针
	if (pa == NULL)
	{
		perror("calloc");
		return 1;
	}
	else
	{
		int *ppa = (int*)realloc(pa, 20);
		if (ppa == NULL)//仍然需要进行判断
			return;
		else
		{
			pa = ppa;//不为空指针的话,可以将原指针指向开辟指针,这样就可以使用原指针变量了
			ppa = NULL;
		}

	}

	//释放空间
	free(pa);
	pa = NULL;
	return 0;
}

4.常见的动态内存的错误

下面就观察常见的一些错误,可能在上边已经叙述过,但还是简单看一下,查漏补缺

4.1对NULL指针的解引用操作

int main()
{
	int* p = (int*)malloc(INT_MAX / 4);//当开辟空间错误时,就会返回空指针
	*p = 20;
	//此时对空指针进行了解引用,最有可能会发生非法访问,导致程序崩溃,原因具体取决于编译器

	//改进方法
	if (p == NULL)
	{
		perror("malloc");
		return 1;
	}

	free(p);
	p = NULL;
	return 0;
}

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

越界访问会造成的问题很多:
1.非法访问,程序崩溃
2.数据损坏:可能覆盖原有内存
3.影响系统稳定性、安全性等,使程序变得不可预测

4.3对非动态开辟的空间使用free释放

这种行为是未定义的,会导致程序崩溃

4.4使用free释放开辟空间的一部分

之前已经谈到过,使用开辟空间指针时,如果对指针的位置进行了更改,就容易发生这种问题,这种情况是未定义的,也会导致程序崩溃

4.5对同一块动态空间多次释放

这种行为是未定义的,被称为双重释放,可能发生违法访问问题,会导致程序崩溃

4.6动态开辟空间忘记释放

会导致内存泄露的问题,这种行为是危险的,比如,对于一台不间断工作的机器,不断存在着内存泄漏的问题,内存一点点地被消耗,会导致程序不间断停止

4.7总结

对于动态开辟的内存:
1.指针必须要检查是否为空
2.要有一个指针指向内存起始地址,保证准确释放
3.开辟空间必须要释放,指针必须要置为空

5.动态内存经典笔试题分析

5.1题目1:

//Test会发生什么结果
void GetMemory(char* p)
{
	p = (char*)malloc(100);
	//传入的是str的地址,p指向str的地址
	//所以对p进行动态内存开辟并不会反应到str上
}

void Test(void)
{
	char* str = NULL;
	GetMemory(str);
	strcpy(str, "hello world");
	//str为空指针,对空指针进行了非法访问
	printf(str);
	//在最后没有对开辟空间进行释放
}

int main()
{
	Test();

	return 0;
}

改进:

//Test会发生什么结果
void GetMemory(char** p)
{
	*p = (char*)malloc(100);
	//二级指针就可以实现str的内存开辟了
}

void Test(void)
{
	char* str = NULL;
	GetMemory(&str);//传入的是一级指针的地址
	//对空指针进行判断
	if (str == NULL)
		perror("malloc");
	else
	{
		strcpy(str, "hello world");
		printf(str);
		free(str);
		str = NULL;
	}
}

int main()
{
	Test();

	return 0;
}

5.2题目2:

char* GetMemory(void)
{
	char p[] = "hello world";
	return p;
}

void Test(void)
{
	char* str = NULL;
	str = GetMemory();
	//返回了数组p的首元素地址,但是数组p的生命周期只在函数中,离开函数会被销毁
	//所以p指向的那块地址是不能被访问的
	printf(str);
}

int main()
{
	Test();

	return 0;
}

5.3题目3:

void GetMemory(char** p, int num)
{
	*p = (char*)malloc(num);
}

void Test(void)
{
	char* str = NULL;
	GetMemory(&str, 100);
	strcpy(str, "hello");
	printf(str);
	//上面的操作其实都对,美中不足的是没有检查空指针,没有释放开辟空间并将指针置为空
	/*free(str);
	str = NULL;*/
}

int main()
{
	Test();

	return 0;
}

5.4题目4:

void Test(void)
{
	char* str = (char*)malloc(100);
	strcpy(str, "hello");
	free(str);
	//内存空间的提前释放,使得开辟的空间不属于你,后续无法再使用了
	//最好的方式是将指针置为NULL,这样就可以通过判断得出指针是否被提前释放了
	if (str != NULL)
	{
		strcpy(str, "world");
		printf(str);
	}
}

int main()
{
	Test();

	return 0;
}

6.柔性数组

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

6.1什么是柔性数组

我们来看一下柔性数组的创建,就知道了什么是柔性数组

struct Book
{
	int a;
	char b;
	char arr[];//结构体前仍然是正常的结构体成员,最后的数组可以不指定数组大小
	//也可以写成char arr[0]
};

6.2柔性数组的特点

1.柔性数组成员前必须存在至少一个成员
2.sizeof结构体时不会包含柔性数组成员
3.包含柔性数组成员的结构用malloc进行动态内存分配,而且分配的大小必须大于结构体的大小,用于柔性数组的使用

比如:

struct Book
{
	int a;
	char b;
	char arr[];//结构体前仍然是正常的结构体成员,最后的数组可以不指定数组大小
};

int main()
{
	printf("%zd", sizeof(struct Book));
	//结果为8,因为没有计算arr的大小
	return 0;
}

6.3柔性数组的使用

struct Book
{
	int a;
	char b;
	char arr[];
};

int main()
{
	struct Book* pb = (struct Book*)malloc(sizeof(struct Book) + 10 * sizeof(int));
	//使用malloc进行动态内存开辟时,包含两个空间:
	//1.sizeof(struct Book),即结构体本身的大小,也就是不算上数组的大小
	//2.10*sizeof(int),它是数组的大小
	if (pb == NULL)
	{
		perror("malloc");
		return 1;
	}
	else
	{
		for (int i = 0; i < 10; i++)
		{
			pb->arr[i] = i;
		}
		for (int i = 0; i < 10; i++)
		{
			printf("%d ", pb->arr[i]);
		}
	}

	free(pb);
	pb = NULL;
	return 0;
}

6.4柔性数组的优势

那为什么要整出一个柔性数组呢,我们可以通过下面的代码实现结构体中数组成员的使用:

struct Book
{
	int a;
	char b;
	int* pb;
};

int main()
{
	struct Book* ppb = (struct Book*)malloc(sizeof(struct Book));
	ppb->a = 4;
	ppb->b = 'a';
	ppb->pb = (int*)malloc(10 * sizeof(int));

	if (ppb->pb == NULL)
	{
		perror("malloc");
		return 1;
	}
	else
	{
		for (int i = 0; i < 10; i++)
		{
			ppb->pb[i] = i;
		}
		for (int i = 0; i < 10; i++)
		{
			printf("%d ", ppb->pb[i]);
		}
	}

	//此时要释放两次
	free(ppb->pb);
	ppb->pb = NULL;
	free(ppb);
	ppb = NULL;
	return 0;
}

这样也可以实现数组的创建,但是,它使用起来并不像柔性数组那样简便,而且,需要注意的是,使用柔性数组所创建的结构体分配的是一块连续的内存!,这样,它既有利于内存释放,也有利于访问速度

7.总结C/C++程序中内存区域的划分

这次只是简单地总结了一下,后续学习过程中可能还会有些许差别,但是现在理解这个已经足够了

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

8.结构体补充

这部分内容全都来源于一篇大佬的文章,链接如下:
链接: link
不幸的是,大佬已经离世,我在此缅怀大佬
我在此只写一下我自己想要列举出来的内容:

struct str {
	int len;
	char a;
	char b;
	char s[0];
};

struct foo {
	struct str* a;
};

int main(int argc, char** argv) {
	struct foo f = { 0 };
	if (f.a->s) {
		//访问成员数组名其实得到的是数组的相对地址,而访问成员指针其实得到的是相对地址里的内容
		//而访问结构体成员其实就是加成员的偏移量
		printf("%x\n", &f.a->len);//表示&str+0x0
		//结果为0
		printf("%x\n", &f.a->b);//表示&str+0x5
		//结果为5
		printf("%x\n", f.a->s);//结果为6
	}
	return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/667806.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

zynq PS点灯

摸鱼碎碎念&#xff1a; 需要做ADC采集并在TFT屏幕实时显示波形&#xff08;简易示波器&#xff09; 发现只使用fpga实现比较困难 使用的是zynq&#xff0c;刚好来把arm部分也学到 参考视频 与 教材文档 01_Zynq SoC FPGA的诞生_哔哩哔哩_bilibili (这是俺点开AXI4接口协…

【小白专用24.5.30已验证】Composer安装php框架thinkPHP6的安装教程

一、框架介绍 1、框架简介和版本选择 Thinkphp是一种基于php的开源web应用程序开发框架ThinkPHP框架&#xff0c;是免费开源的、轻量级的、简单快速且敏捷的php框架。你可以免费使用TP框架&#xff0c;甚至可以将你的项目商用&#xff1b; ThinkPHP8.0 是目前框架正式版的最新版…

windows上CMake、Mingw和VSCode配置调试C/C++代码

大型项目里经常使用CMake&#xff0c;我在Windows平台上开发&#xff0c;使用的代码编辑器是VSCode&#xff0c;我使用的是mingw的编译器&#xff0c;以前使用的是一个脚本来进行编译&#xff1a; run.bat cmake -G "MinGW Makefiles" .. mingw32-makeVSCode可以方…

Linux实验报告(二)——Linux系统中的常用命令

目录 一、实验名称&#xff1a; 二、仪器、设备&#xff1a; 三、参考资料&#xff1a; 四、实验目的&#xff1a; 五、实验内容&#xff08;步骤&#xff09;&#xff1a; 六、实验数据&#xff08;程序&#xff09;记录&#xff1a; ​编辑 ​编辑 七、实验结果分析…

蒙自源六一儿童节特别活动:美食盛宴,快乐无限

蒙自源始终坚持以用户为中心&#xff0c;致力于为消费者提供健康美味的米线。据悉&#xff0c;蒙自源的每家门店店长都会在每天早晨亲自熬制一锅鲜美的汤底&#xff0c;确保顾客能够享受到最新鲜、最美味的米线。为了庆祝六一&#xff0c;蒙自源特意为孩子们准备了一场别开生面…

云数融合与大数据技术在日常生活中的创新应用探索

前言 移动云模型服务产品在中国移动旗下主要包括云计算、大数据、人工智能等服务&#xff0c;它依托广泛的算力资源(4N31X)、丰富的网络接入资源和高品质云专网&#xff0c;实现算网端资源一站式开通&#xff0c;构建企业级一体化解决方案。 文章目录 前言云计算的日常应用智…

六一儿童节与AIGC:科技与童趣的奇妙融

随着人工智能生成内容&#xff08;AIGC&#xff09;技术的发展&#xff0c;越来越多的应用和网站专门为儿童提供学习、游戏和绘画方面的支持。这些平台不仅能够提高孩子们的学习兴趣&#xff0c;还能激发他们的创造力。在六一儿童节即将到来之际&#xff0c;让我们来介绍几款利…

力扣刷题--485. 最大连续 1 的个数【简单】

题目描述 给定一个二进制数组 nums &#xff0c; 计算其中最大连续 1 的个数。 示例 1&#xff1a; 输入&#xff1a;nums [1,1,0,1,1,1] 输出&#xff1a;3 解释&#xff1a;开头的两位和最后的三位都是连续 1 &#xff0c;所以最大连续 1 的个数是 3. 示例 2: 输入&…

颠覆传统:探索Web3对传统计算机模式的冲击

随着Web3技术的崛起&#xff0c;传统计算机模式正面临着前所未有的冲击与挑战。Web3作为下一代互联网的代表&#xff0c;以其去中心化、安全可信的特性&#xff0c;正在颠覆着传统计算机模式的种种假设和局限性。本文将深入探讨Web3对传统计算机模式的冲击&#xff0c;并探索其…

HTML+CSS+JS 熊猫登录表单

效果演示 实现了一个可爱的熊猫登录界面,页面背景使用了渐变色,熊猫的头部和身体使用了圆形和椭圆形的边框,使用了CSS的伪元素和阴影效果来实现熊猫的手和脚。登录框使用了flex布局,包括用户名和密码的输入框和登录按钮,使用了CSS的过渡效果和伪类来实现输入框的动态效果。…

北斗应急救援终端如何做好汛期重点行业安全防控?

【安全提示】 汛期各地高温多雨、极端天气增多 防汛和安全生产形势严峻复杂如何做好汛期重点行业企业安全生产风险防控&#xff1f; 顶坚北斗短报文终端V1单北斗定位终端 北斗应急救援终端在汛期重点行业安全防控中扮演着关键角色&#xff0c;其高可靠性、稳定性和丰富的功能扩…

IdentiFace——多模态人脸识别系统,可捕捉从情绪到性别的所有信息及其潜力

1. 概述 面部识别系统的开发极大地推动了计算机视觉领域的发展。如今&#xff0c;人们正在积极开发多模态系统&#xff0c;将多种生物识别特征高效、有效地结合起来。 本文介绍了一种名为 IdentiFace 的多模态人脸识别系统。该系统利用基于 VGG-16 架构的模型&#xff0c;将人…

国际荐酒师(香港)协会受邀出席广州意大利国庆晚宴

2024年5月30日&#xff0c;意大利驻广州总领事馆举办的2024年意大利国庆招待会及晚宴&#xff0c;庆祝意大利共和国成立。此次晚宴旨在促进中意两国之间的文化交流与合作。国际荐酒师&#xff08;香港&#xff09;协会受主办方邀请参与了这一重要活动。 国际荐酒师&#xff08;…

阿里云语音合成TTS直播助手软件开发

阿里云的TTS比较便宜&#xff0c;效果比不了开源克隆的那种&#xff0c;比纯机器人效果好一点点 阿里云sambert https://help.aliyun.com/zh/dashscope/developer-reference/quick-start-13 Sambert系列模型 1万字1元 &#xff0c;每主账号每模型每月3万字免费 创建API-KEY htt…

SpockMockStatic方法

SpockMockStatic方法 参考: https://blog.csdn.net/knighttools/article/details/44630975 ‍ static方法 import com.meituan.mafka.client.producer.IProducerProcessor; import com.meituan.mdp.langmodel.api.message.AssistantMessage; import com.sankuai.gaigc.arrang…

CentOs-7.5 root密码忘记了,如何重置密码?

VWmare软件版本&#xff1a;VMware Workstation 16 Pro Centos系统版本&#xff1a;CentOS-7.5-x86 64-Minimal-1804 文章目录 问题描述如何解决&#xff1f; 问题描述 长时间没有使用Linux系统&#xff0c;root用户密码忘记了&#xff0c;登陆不上系统&#xff0c;如下图所示…

TOPIAM数字身份管控平台前端技术实践

一、引言 随着企业信息化程度的不断加深&#xff0c;内部办公系统、业务系统及三方SaaS系统的集成与整合成为企业面临的重要挑战之一。特别是如何有效管理员工账号、权限、身份认证以及应用访问&#xff0c;成为保障企业信息安全、提升用户体验的关键。TOPIAM数字身份管控平台…

微信小程序对接发货功能

注&#xff1a;微信小程序对接发货功能 文档地址&#xff1a;https://developers.weixin.qq.com/miniprogram/dev/platform-capabilities/business-capabilities/order-shipping/order-shipping.html php代码 common.php use think\Config; use think\Db; use fast\Http; us…

VS2022+QT5.15.2+MySQL8.4大集合

网上的教程都建议用Qt5&#xff0c;不要用6&#xff0c;不死心的尝试了整整一天失败了&#xff0c;乖乖用回5&#xff0c;qt5需要编译一下生成mysql的动态和静态库 1. mysql8.4安装 下载社区开发版&#xff0c;注意要64位 https://dev.mysql.com/downloads/mysql/ 配置一下数…

nacos连接异常did not find the Leader node;

目录 问题描述解决过程持久化节点真的是存在数据库吗&#xff1f; 问题描述 我搭建的是nacos伪集群&#xff0c;然后主要想着看看集群情况下&#xff0c;临时节点和持久节点的区别。 如果使用临时节点项目能够正常起来&#xff0c;一旦添加ephemeral: false项目就起不来了。 …