​C++内存模型

news2024/12/21 18:42:17

c++语言分区:栈、堆、全局/静态存储区、常量存储区、代码区(.text段)、自由存储区

1、栈区(stack)— 由编译器自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。向下生长
2、堆区(heap) — 一般由程序员分配释放,若程序员不释放,程序结束时可能由OS(操作系统)回收。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表。向上生长
3、全局/静态存储区(static)—,全局变量和静态变量的存储是放在一块的,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域(.bss段),存储了未初始化的全局变量、全局静态变量以及上面提到过的不是内置数据类型的全局常量(例如:const string name = "JIM";)、已初始化的不是内置数据类型全局变量、全局静态变量、类的静态成员变量。

初始化的全局变量和静态变量在一块区域(.data段),存储已初始化的内置数据类型的全局变量、全局静态变量、类的静态成员变量。注意:这里只存储内置数据类型。已初始化的不是内置数据类型全局变量、全局静态变量、类的静态成员变量,存放在bss区的。程序结束后由系统释放。
4、常量存储区(.rodata段) —存储内置数据类型的全局常量、全局静态常量、类的静态成员常量,"Hello World"这种字面常量等。常量字符串就是放在这里的,虚函数表也在这里。程序结束后由系统释放。注意:这里只存储内置数据类型,内置数据类型是有原子性的,比如:int,float,double等,const string name = "JIM";因为string是抽象数据类型,不是内置数据类型,是存放在.bss区的。
5、代码区(.text段)—存放程序的编译后的可执行代码的地方了,CPU执行的机器指令,并且是只读的。我们编译生成的可执行文件二进制代码就存放在这个区,例如:全局函数,类的成员函数(包括静态成员函数,非静态成员函数,虚函数)。读写权限:r-x存放代码(如函数),不允许修改(类似常量存储区),但可以执行(不同于常量存储区)。

6.自由存储区:自由存储是C++中通过new与delete动态分配和释放对象的抽象概念,自由存储区和堆不是一个概念。一般而言,自由存储区是指CRT(C运行时库)通过malloc,free函数管理的内存。堆特指通过new,delete管理的内存。但是在部分编译器的实现上这两块内存都是同一种管理方式
 


 

注意:#define BUF_MAX 1024;enum DAY{FRI = 5}; 中的BUF_MAX和FRI是在其作用域内,在预编译程序阶段就将它们展开,代码中出现BUF_MAX的地方用1024替换,FRI出现的地方用5替换,所以BUF_MAX和FRI在程序中是没有地址空间的。

共享库
libc.so,libstdc++.so等等程序中用到的共享库映射区

下面,我们验证一下C++程序中的内存是不是按照上面所说的那么分配管理的。

#include <iostream>
#include <unistd.h>
#include <string>
#include <cstdio>
using namespace std;
 
extern char **environ; //环境变量
 
#define BUF_MAX 1024 //宏,在程序预编译阶段编译器会将出现BUF_MAX的地方用1024替换掉
enum DAY
{
    FRI = 5 //FRI是枚举数值,在程序预编译阶段编译器会将出现FRI的地方用5替换掉
};
 
void g_func_test() //全局函数
{
    static int local_static_i = 10;
    cout << "&local_static_i = " << &local_static_i << endl;
    cout << "test" << endl;
}
 
const int gc_i = 100; //内置数据类型全局常量
static const int gsc_i = 50; //内置数据类型全局静态常量
 
int g_i = 1; //已初始化内置数据类型全局变量
static int gs_i = 10; //已初始化内置数据类型全局静态变量
 
int g_uninit_i; //未初始化的全局变量,默认初始化g_uninit_i = 0;
static int gs_uninit_i; //未初始化的全局静态变量,默认初始化gs_uninit_i = 0;
int *pg_uninit_i; //未初始化的全局指针变量,默认初始化为pg_uninit_i = NULL;
string g_name = "JIM"; //已初始化抽象数据类型全局变量
const string gc_hello = "Hello World"; //抽象数据类型全局常量
static const string gsc_hello = "Hello World"; //抽象数据类型全局静态常量
 
class Test
{
    public:
    Test():m_num(0),mc_num(1)
    {
 
    }
 
    void test() const
    {
        cout << "Hello World" << endl;
        printf("&\"Hello World\" = %p\n", &"Hello World");
    }
    
    const int mc_num; //内置数据类型常量,必须在构造函数中初始化
    static const int msc_num; //内置数据类型静态常量,必须类外初始化
    static const string msc_name; //抽象数据类型静态常量,必须类外初始化
 
    int m_num; //内置数据类型变量
    static int ms_num; //内置数据类型静态变量
};
 
const int Test::msc_num = 2;
const string Test::msc_name = "JIM";
int Test::ms_num = 1;
 
const Test gc_test; //抽象数据类型全局常量
 
int main(int argc, char *argv[])
{
    cout << "---命令行参数,环境变量---" << endl;
    cout << "environ = " << environ << endl;  //环境变量
    cout << "argv = " << argv << endl; //命令行参数
    cout << endl; 
 
    cout << "---------栈区--------" << endl;
    int temp1 = 1; //局部变量,存储在栈区
    int temp2 = 2; //局部变量,存储在栈区
    const int ctemp = 3; //局部常量,存储在栈区
    cout << "&temp1 = " << &temp1 << endl;
    cout << "&temp2 = " << &temp2 << endl;
    cout << "&ctemp = " << &ctemp << endl;
    cout << endl;
 
 
    cout << "---------堆区--------" << endl;
    int *p1 = new int(1); //堆中开辟空间
    int *p2 = new int(2); //堆中开辟空间
    cout << "p1 = " << p1 << endl;
    cout << "p2 = " << p2 << endl;
    delete p1;
    p1 = NULL;
    delete p2;
    p2 = NULL;
    cout << endl;
 
    cout << "-----.bss段------" << endl;
    cout << "&g_uninit_i = " << &g_uninit_i << endl; //未初始化的全局变量,存储在.bss段
    cout << "g_uninit_i = " << g_uninit_i << endl;
    cout << "&gs_uninit_i = " << &gs_uninit_i << endl; //未初始化的全局静态变量,存储在.bss段
    cout << "gs_uninit_i = " << gs_uninit_i << endl;
    cout << "&pg_uninit_i = " << &pg_uninit_i << endl; //未初始化的全局指针变量,存储在.bss段
    cout << "pg_uninit_i = " << pg_uninit_i << endl;
    //cout << "*pg_uninit_i = " << *pg_uninit_i << endl; //默认初始化为NULL,默认指向了内存地址为0,访问会出现断错误
    cout << "&g_name = " << &g_name << endl; //已初始化抽象数据类型全局变量,存储在.bss段
    cout << "&gc_hello = " << &gc_hello << endl; //抽象数据类型全局常量,存储在.bss段
    cout << "&gsc_hello = " << &gsc_hello << endl; //抽象数据类型全局静态常量,存储在.bss段
    cout << "&gc_test = " << &gc_test << endl; //抽象数据类型全局常量,存储在.bss段
    cout << "&Test::msc_name = " << &Test::msc_name << endl; //类的抽象数据类型静态常量,存储在.bss段
    //g_test.test();
    cout << endl;
 
    cout << "---------.data段--------" << endl;
    cout << "&g_i = " << &g_i << endl; //已初始化内置数据类型全局变量,存储在.data段
    cout << "&gs_i = " << &gs_i << endl; //已初始化内置数据类型全局静态变量,存储在.data段
    //cout << "&local_static_i = " << &local_static_i << endl; //函数体内定义的局部静态变量是在栈空间
    cout << "&Test::ms_num = " << &Test::ms_num << endl; //类的内置数据类型静态变量,存储在.data段
    cout << endl;
 
    cout << "-------.rodata段-------" << endl;
    cout << "&gc_i = " << &gc_i << endl; //内置数据类型全局常量,存储在.rodata段
    cout << "&gsc_i = " << &gsc_i << endl; //内置数据类型全局静态常量,存储在.rodata段
    //printf("&Test::mc_num = %p\n", &Test::mc_num); //类的成员常量,只有类被实例化后才存在的
    cout << "&Test::msc_num = " << &Test::msc_num << endl; //类的内置数据类型静态常量,存储在.rodata段
    printf("&\"Hello World\" = %p\n", &"Hello World"); //字面常量,存储在.rodata段
    cout << endl;
 
    cout << "-------.text段-----" << endl;
    printf("&g_func_test = %p\n", &g_func_test); //全局函数,在代码区
    printf("&Test::test = %p\n", &Test::test); //类的成员函数,在代码区
    printf("&printf = %p\n", &printf); //libc库中的函数,为什么打印的函数地址是在代码区?
    cout << endl;
 
    //g_func_test();
 
    //写成死循环,是为了让进程一直在运行,方便我们在bash中输入命令,查看进程的相关信息
    while(1) 
    {
        sleep(1);
    }
 
    return 0;
}

---命令行参数,环境变量---
environ = 0xd448f0 //环境变量
argv = 0xd46f80 // 命令行参数

---------栈区--------
&temp1 = 0x61fdfc // 局部变量,存储在栈区
&temp2 = 0x61fdf8 // 局部变量,存储在栈区
&ctemp = 0x61fdf4 // 局部常量,存储在栈区

---------堆区--------
p1 = 0xd412b0 // 堆中开辟空间
p2 = 0xd412d0 // 堆中开辟空间

-----.bss段------
&g_uninit_i = 0x40d040 // 未初始化的全局变量,存储在.bss段
g_uninit_i = 0 // 未初始化的全局变量,初值默认为0
&gs_uninit_i = 0x40d0a4 // 未初始化的全局静态变量,存储在.bss段
gs_uninit_i = 0 // 未初始化的全局静态变量,初值默认为0
&pg_uninit_i = 0x40d048 // 未初始化的全局指针变量,存储在.bss段
pg_uninit_i = 0 // 未初始化的全局指针变量,初值默认为0
&g_name = 0x40d060 // 已初始化抽象数据类型全局变量,存储在.bss段
&gc_hello = 0x40d0c0 // 抽象数据类型全局常量,存储在.bss段
&gsc_hello = 0x40d0e0 // 抽象数据类型全局静态常量,存储在.bss段
&gc_test = 0x40d100 // 抽象数据类型全局常量,存储在.bss段
&Test::msc_name = 0x40d080 // 类的抽象数据类型静态常量,存储在.bss段

---------.data段--------
&g_i = 0x409014 // 已初始化内置数据类型全局变量,存储在.data段
&gs_i = 0x409018 // 已初始化内置数据类型全局静态变量,存储在.data段
&Test::ms_num = 0x40901c // 类的内置数据类型静态变量,存储在.data段

-------.rodata段-------
&gc_i = 0x40a01c内置数据类型全局常量,存储在.rodata段
&gsc_i = 0x40a020内置数据类型全局静态常量,存储在.rodata段
&Test::msc_num = 0x40a048 // 类的内置数据类型静态常量,存储在.rodata段
&"Hello World" = 000000000040a024 // 字面常量,存储在.rodata段

-------.text段-----
&g_func_test = 00000000004015a4 // 全局函数,在代码区
&Test::test = 000000000061fde0// 类的成员函数,在代码区
&printf = 0000000000401550 // libc库中的函数打印的函数地址是在代码区

.text段:存储全局函数,类的成员函数编译生成的可执行二进制文件

.rodata段:存储内置数据类型的全局常量(const int gc_i = 100;)、全局静态常量(static const int gsc_i = 50;)、类的静态常量

.text段往上是.rodata段,因为打印出的全局函数,类的成员函数的在内存地址隔了一定的地址空间,并且都比常量在内存地址小。

已初始化全局变量区(.data段)、未初始化全局变量区(.bss段)

.data段:存储已初始化内的置数据类型的全局变量、全局静态变量、类的静态成员变量

.bss段:存储未初始化的全局变量、全局静态变量,以及抽象数据类型的全局常量、全局静态常量、类的静态成员常量和已初始化的抽象数据类型的全局变量、全局静态变量、类的静态成员变量。

.data段往上是.bss段,因为打印出的已初始化的变量的内存地址隔了一定的地址空间,并且都比未初始化的变量的内存地址小。

p1和p2的内存地址范围都在0x0000000001e1f000 - 0x0000000001e40000堆区范围内,说明是在堆区开辟的空间

堆的内存地址是往上生长的,p2后new的内存地址比p1先new的内存地址大

temp1、temp2、ctemp这3个临时变量的地址范围都在0x007ffd65b75000 - 0x007ffd65b96000栈区范围内,说明是在栈区分配的空间。

temp1、temp2、ctemp这3连续在栈上开辟的临时变量的地址,依次减少,说明栈是向下生长的

environ、argv的地址范围都在0x007ffd65b75000 - 0x007ffd65b96000栈区范围内,说明存储命令行参数和环境变量的地址也是在栈区分配的。

p1和p2的内存地址范围都在0x0000000001e1f000 - 0x0000000001e40000堆区范围内,说明是在堆区开辟的空间

堆的内存地址是往上生长的,p2后new的内存地址比p1先new的内存地址大

const 

1、const修饰的量不是常量,仅仅是个只读量,其本质仍为变量,无法用来初始化 array 容器。在编译的时候全部替换const变量被赋予的值(这点和C语言的宏相似),在运行的时候该const变量可通过内存进行修改:

1.1)通过内存(指针)可以修改位于栈区的const变量,语法合乎规定,编译运行不会报错,但是在编译的时候所有用到该常量的地方全部被替换成了定义时所赋予的值,然后再运行的时候无法使用通过指针修改后的值。


1.2)通过内存(指针)修改位于静态存储区的的const变量,语法上没有报错,编译不会出错,一旦运行就会报告异常。

const int a = 1;
void test1()
{
 const int c = 3;
 int* p = (int*)& c;
 *p = 4;
 //在编译的时候c被替换成了3,无法使用通过内存修改后的值
 //*p表示c被修改后的值
 cout << c << " " << *p << endl;
 
 p = (int*)& a;
 //在编译的时候a被替换成了1,p指向a的地址
 cout << a << " " << *p << endl;
 //修改位于静态存储区的const变量,编译无错,运行就会报异常
 *p = 4;
 //a的值还是1,因为在编译的时候字母a就被替换成1了,运行时会报异常
 cout << a << endl;
}

const volatile修饰的变量,可以在编译时不用全部替换被const volatile变量被赋予的值,因此可以在运行时使用通过内存修改后的值:

2.1)通过内存(指针)可以修改位于栈区的const volatile变量,语法合乎规定,编译运行不会报错,在编译的时候所有用到该常量的地方不会替换成了定义时所赋予的值,在运行的时候可以使用通过指针修改后的值。
2.2)通过内存(指针)修改位于静态存储区的的const volatile变量,语法上没有报错,编译不会出错,一旦运行就会报告异常。

void test2()
{
 const volatile int c = 3;
 //const volatile被修饰的变量只有在运行的时候才使用其在初始化时被赋予的值
 //编译的时候,const volatile修饰的变量不替换
 cout << c << endl;
 void* p = (void*)&c;
 (*(int*)p) = 4;
//修改c的值为4
 cout << c << endl;
 
 p = (void*)& b;
 //在运行的时候修改静态存储区的const volatile变量编译不出错,运行报错
 (*(int*)p) = 5;
 cout << b << endl;
 } 
 

注:通过指针修改在全局区上的const变量,编译可通过,运行就会报异常。

C++ 11标准中,为了解决 const 关键字的双重语义问题,保留了 const 表示“只读”的语义,而将“常量”的语义划分给了新添加的 constexpr 关键字。因此 C++11 标准中,建议将 const 和 constexpr 的功能区分开,即凡是表达“只读”语义的场景都使用 const,表达“常量”语义的场景都使用 constexpr。

void dis_1(const int x){
    //错误,x是只读的变量
    array <int,x> myarr{1,2,3,4,5};
    cout << myarr[1] << endl;
}

void dis_2(){
    const int x = 5;
    array <int,x> myarr{1,2,3,4,5};
    cout << myarr[1] << endl;
}

“只读”和“不允许被修改”之间并没有必然的联系

#include <iostream>
using namespace std;

int main()
{
    int a = 10;
    const int & con_b = a;
    cout << con_b << endl;

    a = 20;
    cout << con_b << endl;
}

可以看到,程序中用 const 修饰了 con_b 变量,表示该变量“只读”,即无法通过变量自身去修改自己的值。但这并不意味着 con_b 的值不能借助其它变量间接改变,通过改变 a 的值就可以使 con_b 的值发生变化。

但在某些场景中,必须明确使用 constexpr,例如:

#include <iostream>
#include <array>
using namespace std;

constexpr int sqr1(int arg){
    return arg*arg;
}

const int sqr2(int arg){
    return arg*arg;
}

int main()
{
    array<int,sqr1(10)> mylist1;//可以,因为sqr1时constexpr函数
    array<int,sqr2(10)> mylist1;//不可以,因为sqr2不是constexpr函数
    return 0;
}

其中,因为 sqr2() 函数的返回值仅有 const 修饰,而没有用更明确的 constexpr 修饰,导致其无法用于初始化 array 容器(只有常量才能初始化 array 容器)。
 

扩展:

解决vscode终端输出中文乱码问题

更改vscode默认编码UTF-8为GBK,(该法需确认系统编码环境为GBK格式,cmd终端输入chcp可以查看当前系统默认编译器,65001代表UTF-8,936代表GBK;设置完仍需重启vscode否则仍会出问题。)

已经写好的程序文件,通过右下角更改:选通过编码保存,保存为GBK格式重新运行程序即解决乱码问题,

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

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

相关文章

iSlide2024一款基于PPT的插件工具包含38个设计辅助功能

根据使用者情况表明iSlide 是一款拥有30W素材的PPT高效设计软件&#xff0c;可提高90%工作效率&#xff0c;现全球已有超过1400万使用者&#xff0c;智能排版原创高品模板可商用图形&#xff0c;真正摆脱PPT的束缚&#xff0c;把精力用在该用的地方。我们都明白islide插件功能特…

Linux背景介绍与环境搭建

本章内容 认识 Linux, 了解 Linux 的相关背景学会如何使用云服务器掌握使用远程终端工具 xshell 登陆 Linux 服务器 Linux 背景介绍 发展史 本门课程学习Linux系统编程&#xff0c;你可能要问Linux从哪里来&#xff1f;它是怎么发展的&#xff1f;在这里简要介绍Linux的发展…

pytorch 中 nn.Conv2d 解释

1. pytorch nn.Con2d 中填充模式 torch.nn.Conv2d(in_channels, out_channels, kernel_size, stride1, padding0, dilation1, groups1, biasTrue, padding_mode‘zeros’, deviceNone, dtypeNone) 1.1 padding 参数的含义 首先 &#xff0c;padd N, 代表的是 分别在 上下&…

阿里云安全恶意程序检测

阿里云安全恶意程序检测 赛题理解赛题介绍赛题说明数据说明评测指标 赛题分析数据特征解题思路 数据探索数据特征类型数据分布箱型图 变量取值分布缺失值异常值分析训练集的tid特征标签分布测试集数据探索同上 数据集联合分析file_id分析API分析 特征工程与基线模型构造特征与特…

driver.find_element()用法

driver.find_element()用于在Web页面中定位单个元素。它是Selenium WebDriver库中的 一种方法。该方法接受一个定位器&#xff08;locator&#xff09;和一个值作为参数&#xff0c;用于指定要查找的元素 位置。下面是具体的用法和一些例子&#xff1a; 通过ID定位元素&#x…

AI神助攻,购物更省心:我即将上线一套企业数据高度契合的智能导购APP来开创这一新纪元

将要做什么事的介绍 近期博客写了少了&#xff0c;是因为近小半年来我正在打造一款可私布在企业内部并结合企业自有领域&#xff08;零售商超先行&#xff09;数据的智能导购引擎。截止目前为止还算顺利&#xff0c;并且我将很快将在中国本土的一家生鲜百货超市上线这一款生成…

[iOS开发]iOS中TabBar中间按钮凸起的实现

在日常使用app的过程中&#xff0c;经常能看到人家实现了底部分栏控制器的中间按钮凸起的效果&#xff0c;那么这是怎么实现的呢&#xff1f; 效果演示&#xff1a; 实现原理&#xff1a; 创建按钮 创建一个UITabBar的子类&#xff0c;重写它的layoutSubviews方法&#xff1…

redis源码分析之IO多路复用

文章目录 1、简述2、多路复用的三个函数3、创建epoll实例4、绑定端口、监听端口5、向epoll实例注册连接事件6、从epoll实例中获取就绪的事件 1、简述 众所周知&#xff0c;redis是一款抗高并发的利器&#xff0c;据官方压测&#xff0c;单机可达10万qps。但背后实际处理命令的…

字典与数组第八讲:工作表数据计算时为什么要采用数组公式(二)

《VBA数组与字典方案》教程&#xff08;10144533&#xff09;是我推出的第三套教程&#xff0c;目前已经是第二版修订了。这套教程定位于中级&#xff0c;字典是VBA的精华&#xff0c;我要求学员必学。7.1.3.9教程和手册掌握后&#xff0c;可以解决大多数工作中遇到的实际问题。…

具有自主产权的SaaS门店收银系统全套源码输出

PHPMysql前后端分离&#xff0c; 小程序线上商城&#xff1b; 进销存管理库存盘点&#xff0c; 多仓库库存调拨&#xff0c; 会员系统。 消费者扫码查价系统。

外卖行业如何借助微信管理系统实现高效运营

摘要&#xff1a;本文将介绍微信管理系统在外卖行业的应用&#xff0c;包括聚合聊天、朋友圈营销和群发功能。通过这些功能&#xff0c;外卖商家可以更高效地管理订单、与客户沟通、推广品牌和增加销售额。 一、引言 随着外卖行业的快速发展&#xff0c;竞争也日益激烈。为了…

前端面试题之HTML篇

1、src 和 href 的区别 具有src的标签有&#xff1a;script、img、iframe 具有href的标签有&#xff1a;link、a 区别 src 是source的缩写。表示源的意思&#xff0c;指向资源的地址并下载应用到文档中。会阻塞文档的渲染&#xff0c;也就是为什么js脚本放在底部而不是头部的…

Vert.x学习笔记-Vert.x的基本处理单元Verticle

Verticle介绍 Verticle是Vert.x的基本处理单元&#xff0c;Vert.x应用程序中存在着处理各种事件的处理单元&#xff0c;比如负责HTTP API响应请求的处理单元、负责数据库存取的处理单元、负责向第三方发送请求的处理单元。Verticle就是对这些功能单元的封装&#xff0c;Vertic…

数据中心系统解决方案

设计思路 系统设计过程中充分考虑各个子系统的信息共享要求&#xff0c;对各子系统进行结构化和标准化设计&#xff0c;通过系统间的各种联动方式将其整合成一个有机的整体&#xff0c;使之成为一套整体的、全方位的数据中心大楼综合管理系统&#xff0c;达到人防、物防和技防…

MySQL8.0.26-unbuntu版安装

MySQL8.0.26-ubuntu版安装 在这里会有一个坑&#xff0c;就是我在安装的时候,是按照另外一种版本的安装&#xff0c;报错没有rpm这个包&#xff0c;然后我就去下载&#xff0c;然后就报错 E: 无法定位软件包 &#xff0c;害的我找了好久的资料&#xff0c;一直没有解决&#x…

2023-11-04:用go语言,如果n = 1,打印 1*** 如果n = 2,打印 1*** 3*** 2*** 如果n = 3,打印

2023-11-04&#xff1a;用go语言&#xff0c;如果n 1&#xff0c;打印 1*** 如果n 2&#xff0c;打印 1***3*** 2*** 如果n 3&#xff0c;打印 1***3*** 2***4*** 5*** 6*** 如果n 4&#xff0c;打印 1***3*** 2***4*** 5*** 6***10** 9*** 8*** 7*** 输入…

SoftwareTest5 - 你就只知道功能测试吗 ?

你就只知道功能测试吗 ? 一 . 按照测试对象划分1.1 文档测试1.2 可靠性测试1.3 容错性测试1.4 安装卸载测试1.5 内存泄漏测试1.6 弱网测试 二 . 按是否查看代码划分2.1 黑盒测试2.2 白盒测试2.3 灰盒测试 三 . 按照开发阶段划分3.1 单元测试3.2 集成测试3.3 冒烟测试3.4 系统测…

如何通过智能管理箱实现高效文件管理:关键字轻松修改文件名

在信息化时代&#xff0c;文件管理变得尤为重要。智能管理箱已经成为我们生活中不可或缺的一部分。它可以帮助我们高效地管理各种文件&#xff0c;使得我们的工作和生活更加便捷。是一种高效的文件管理工具&#xff0c;可以帮助我们轻松地整理和分类文件&#xff0c;提高工作效…

【算法】昂贵的聘礼(dijkstra算法)

题目 年轻的探险家来到了一个印第安部落里。 在那里他和酋长的女儿相爱了&#xff0c;于是便向酋长去求亲。 酋长要他用 10000 个金币作为聘礼才答应把女儿嫁给他。 探险家拿不出这么多金币&#xff0c;便请求酋长降低要求。 酋长说&#xff1a;”嗯&#xff0c;如果你能够替我…

SpringBoot+AOP+自定义注解,优雅实现日志记录

文章目录 前言准备阶段1、数据库日志表2、自定义注解编写3、AOP切面类编写4、业务层4.1、Service 层&#xff1a;4.2 Service 实现层&#xff1a; 5、测试 前言 首先我们看下传统记录日志的方式是什么样的&#xff1a; DeleteMapping("/deleteUserById/{userId}") …