自定义博客皮肤VIP专享

*博客头图:

格式为PNG、JPG,宽度*高度大于1920*100像素,不超过2MB,主视觉建议放在右侧,请参照线上博客头图

请上传大于1920*100像素的图片!

博客底图:

图片格式为PNG、JPG,不超过1MB,可上下左右平铺至整个背景

栏目图:

图片格式为PNG、JPG,图片宽度*高度为300*38像素,不超过0.5MB

主标题颜色:

RGB颜色,例如:#AFAFAF

Hover:

RGB颜色,例如:#AFAFAF

副标题颜色:

RGB颜色,例如:#AFAFAF

自定义博客皮肤

-+

Zebra的博客

Zebra的博客

  • 博客(126)
  • 资源 (2)
  • 收藏
  • 关注

原创 MySQL~索引的优缺点是什么?有哪些优化索引的方法?

优点:提高查询记录的速度。经常作为查询条件的字段经常放到GROUP BY或者ORDER BY后面的字段不经常作为查询条件的字段不经常重复率高的字段表数据量很少经常需要更新。

2023-07-14 16:17:04 808

原创 MySQL为什么采用B+树作为索引底层数据结构?

为什么采用B+树作为索引的数据结构?1.和传统的二叉查找树相比,B+树是一棵多叉树,树的高度更小,整个树更加矮胖,查询的效率更高;二叉树的话,数据量上去了树的高度就会很高。(tips:实际使用中,m的值会超过100,此时即便是千万级的数据量,仍然可以保证在3-4次IO内就找到数据)2.和B树相比B+树的磁盘IO效率更高。B+树的数据只会存放在叶子结点(非叶子节点只存索引信息),而B树在每个节点上都要存放数据,所以在相同的空间内,B+树可以存放更多地索引信。

2023-07-11 21:52:13 841

原创 关于stl容器的迭代器失效问题

在项目中使用stl容器的时候,多线程环境下出错,调试很久发现问题是使用容器的时候由于容器扩容导致的线程不安全,还有扩容导致的迭代器失效问题,于是就想着把迭代器失效的问题总结一下。我在项目开发中使用vector时,由于扩容导致的迭代器失效。运行结果:可以看到,iter迭代器一开始指向的元素是1,但是由于vector底层扩容,导致元素从一个空间迁移到另一个空间,原来的空间释放,iter指向了已经释放的内存(悬挂指针),从而导致结果出错。

2023-04-24 00:18:44 705 1

原创 *(void**)解析——如何设计可以在32位下访问到内存区域的前4个字节,在64位下访问到前8个字节?

最近在写项目的时候遇到这样一个场景:需要管理多个空闲的内存块,把它们以链表的形式连接起来,那就需要在内存块的头4个字节(32位下)存放下一个内存块的地址。

2023-03-31 23:32:19 453

原创 使用C++11的Thread和condition_variable实现单例模式的线程池

首先我们来看一下线程池的大致结构,这里先不实现单例模式,线程池使用了模板类,可以用于任意类型的任务对象,其中有一些成员变脸,_task_queue用于保存线程池中要执行的任务,_count表示线程的数量,_isStop表示线程池是否要停止运行,停止线程执行任务,还有一个互斥锁用于保证线程对任务队列的访问时安全的,条件变量用于当任务队列为空时,让线程等待;tips:任务队列中的任务是T类型的指针,如果任务执行完就不需要的话,我们可以在这里delete释放任务对象占用的空间,当然也可以由主线程去释放。

2023-03-14 00:10:14 417 1

原创 关于new和delete的一些思考,为什么不能在析构函数中调用delete释放对象的内存空间,new和delete的原理

最近在写代码的时候,觉得每次new出来的对象都需要去delete好麻烦,于是直接把delete写到了析构函数中,在析构函数里面写了句delete this,结果调用析构函数的时候死循环了,不是很理解原因,于是去研究了一下。

2023-03-08 17:10:32 1020

原创 TCP协议详解—TCP各个报头属性的作用

client给server发送消息,server给client应答的时候,除了填上确认序号表示收到client发送的确认序号之前的所有报文,还要发送一些数据给client,比如我收到了,那你要这样处理等等,这时候携带了数据,为了保证client端收到数据的有序性,那就要填上序号了。面对大量的TCP报文,对不同类型的报文要进行不同的处理,如何区分各个报文的类别?2.只有OS的 TCP协议可以知道网络,乃至对方的网络状态明细,所以,也有只有TCP协议,能处理如何发,什么时候发,发多少,出错了怎么办等细节问题。

2023-03-01 13:08:07 2522

原创 https加密原理详解,带你搞懂它为什么比http更安全

https还使用了数字摘要的技术来验证接收到的内容是否完整,即是否被中间人篡改过。https解决这个问题就是使用数字摘要和数字签名的技术,就和前面提到的一样,发送方将要发送的内容通过单向哈希算法生成数字摘要,然后使用自己的私钥加密生成数字签名,发送到对方手里后(通过对称秘钥加密传输的,不用说),接收方用发送方的公钥解密生成数字摘要1,然后对明文数据内容用相同的哈希算法生成数字摘要2,对比两个数字摘要,如果相等就是没有被修改,如果不等就是被修改了。http是直接与网络层的tcp协议通信的。

2023-02-22 20:28:06 415 1

原创 室友打了一把王者的时间,我理清楚了grep,find,管道|,xargs的区别与联系,用的时候为什么要这样用

这也就解释了为什么用grep的时候,需要先ls -Rl,因为grep搜索的匹配条件是行,所以需要先把一条条文件信息列出来,把ls的结果通过管道|传给grep去搜索。 而管道|就是将前面的标准输出转换成后一条指令的标准输入,但是后一条指令不一定能够读取标准输入,例如 kill , rm 这些程序如果命令行参数中没有指定要处理的内容则不会从标准输入中读取xargs就可以将前一条指令的标准输出的字符串,根据空格分割成命令行参数,并以命令行参数的形式交给后一条指令,从而使得一些

2023-02-18 16:44:18 518

原创 序列化和反序列化~如何实现自定义协议?jsoncpp的使用

什么是协议?双方约定好的结构化的数据,本质就是协议的表现。(相当于我们约定好,哪个字段是昵称,哪个是头像,哪个是消息等等)实现一个简易的计算器服务,能处理client发送过来的加减乘除等基本计算请求。client每次向server发送的请求结构包含两个操作数和一个操作符的字符串;而server每次给client的请求包含一个code表示状态码,一个result表示计算结果。因此,我们可以在自定义协议文件中定义如下两个结构体,这就是我们自己定义的协议,server和client都必须遵守。

2023-02-12 17:23:02 634

原创 HTTP协议基础知识(URL,请求响应格式,状态码,方法,cookie和session等)

首先我们需要知道以下几点,1.我们请求的图片,html,css,js,视频,音频,标签,文档,等这些都称之为"资源"。2.服务器后台,是用Linux做的。3.IP + Port唯一的确定一个进程。4.公网IP地址是唯一确认一台主机的,而我们所谓的网络"资源"都一定是存在于网络中的一台Linux机器上。Linux或者传统的操作系统,保存资源的方式,都是以文件的方式保存的。单Linux 系统,标识一个唯一资源的方式,就是通过路径5.所以:IP+Linux路径,就可以唯一的确认一个网络资源。

2023-02-10 14:38:57 1014

原创 Linux下socketAPI的使用,实现udp和tcp通信

/ 创建 socket 文件描述符 (TCP/UDP, 客户端 + 服务器)一个文件描述符,执行socket()函数创建套接字的时候,相当于把网络以文件的形式打开了。创建失败就返回小于0的数// 绑定端口号 (TCP/UDP, 服务器)如果绑定成功返回0,绑定失败返回-1注意:bind(sock, (struct sockaddr*)&local, sizeof(local))传入第二个参数的时候需要进行强转。

2023-02-04 17:54:08 1462

原创 多线程~实现一个自己的线程池,以及基于单例模式的线程池

池化技术本质上都是为了提高效率。线程池也是同理,提前准备好一些线程,用来随时处理任务(处理完任务又放回池子),就称为线程池。为了提高效率。避免了在处理短时间任务时由于创建与销毁线程影响任务处理效率如果没有线程池,比如用户向服务器发起请求的时候,服务器收到请求需要先创建线程(需要耗时),然后再给用户提供服务,这对用户来说就是不必要的时间消耗。因此,可以提前准备好一些线程,服务器收到用户请求的时候马上就可以从线程池拿出一个线程来给用户提供服务。

2023-01-17 12:10:30 1035

原创 多线程~POSIX信号量实现生产者消费者模型,PV操作

信号量本质就是一把计数器,描述临界资源中资源数目的大小,合理使用可以达到对临界资源进行预订的目的。(最多能有多少资源分配给线程)多线程预定资源的方法:临界资源如果可以被划分成为一个一个的小资源,如果处理得当,我们也有可能让多个线程同时访问临界资源的不同区域,从而实现并发并行的访问临界资源。

2023-01-13 21:48:54 816

原创 多线程-生产者消费者模型,条件变量的使用,阻塞队列的实现

因为我们在调用pthread_cond_wait的时候,一般都是占用着锁的(一般都是检测到临界资源,发现不满足条件,才会调用这个wait方法),此时如果不把锁释放,直接wait的话,那就死锁了,其他线程也无法获取锁,修改这个临界资源。当消费者Pop取走任务的时候,同理要判断队列是否为空,如果空了就让消费者等待,然后加锁对队列进行操作,最后唤醒生产者来生产队列中的任务。暂时等待,而不是一直尝试抢票,如果一直抢票,抢票线程会一直获取锁,然后判断是否有票,没票,释放锁,不停循环这个操作,做无用功,而且占用锁。

2023-01-05 18:45:48 528

原创 操作系统~Linux~线程的互斥,mutex互斥锁的使用及其原理

1.临界资源:凡是被线程共享访问的资源都是临界资源(多线程、多进程打印数据到显示器,显示器就是临界资源)2.临界区:代码中访问临界资源的代码(在代码中,不是所有的代码都是进行访问临界资源的。而访问临界资源的代码区域我们称之为临界区)3.对临界区进行保护的功能,本质就是对临界资源的保护。方式:互斥或者同步4.互斥:在任意时刻,只允许一个执行流访问某段代码(访问某部分资源〉,就可以称之为互斥5.同步:一般而言,让访问临界资源的过程在安全的前提下(一般都是互斥and原子的),让访问资源具有一定的顺序性(具有合理性

2022-12-31 16:40:00 2342

原创 操作系统~Linux~线程控制,POSIX线程库的使用

功能:创建一个新的线程原型int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void*), void *arg);参数返回值:成功返回0;失败返回错误码pthread_self()函数可以返回当前线程的id,这是一个地址,所以我们用十六进制打印这个地址结果分析 可以看到,主线程的线程id和新线程的线程id是不同的,而且是地址,同时我们可以发现参数zebra成

2022-12-26 18:08:03 757 1

原创 Linux~操作系统~什么是线程?线程底层结构是怎样的?线程的优缺点?

把程序文件加载到内存,操作系统为该进程创建PCB,页表等管理进程的数据机构,这就形成了进程,是程序的执行实例。(一个程序可以有多个进程来执行)是在进程内部运行的一个执行分支(执行流),属于进程的一部分,粒度要比进程更加细和轻量化。进程是操作系统资源分配的基本单位,线程是CPU调度的基本单位。

2022-12-21 14:24:59 501

原创 Linux操作系统~信号处理的底层原理

执行用户提供的信号处理函数handler。比如我们要处理信号4,此时我们处于内核态,根据4号信号的handler表里面找到用户定义的handler函数地址,然后切换到用户态执行信号处理函数(这里调用handler对操作系统来说相当于是一次回调)。

2022-12-18 15:39:17 490

原创 Linux操作系统~进程崩溃的原理是什么?信号的产生方式有哪些?

signal函数:修改进程对信号的默认处理动作signum:表示要修改几号信号handler表示要做出的处理动作函数e.g.://传入的信号表示要对几号信号进行自定义处理{//打印执行的是几号信号的处理函数,打印执行}int main(){//通过signal注册2号信号的处理动作,改成自定义处理动作while(1){sleep(1);}return 0;}

2022-12-09 20:01:05 1780

原创 Linux操作系统~基于systemV共享内存的进程间通信

首先我们先来看一下进程间通信总共有哪些方式?管道System V IPCPOSIX IPC这里我讨论的是基于systemV共享内存的进程间通信 命名管道和匿名管道都是基于文件的通信方式,还有systemV标准的通信方式:在同一主机内的进程间通信方案——system V方案注意进程间通信的本质:让不同进程看到同一份资源1.通过某种调用,在内存中创建一份内存空间2.通过某种调用,让进程(参与通信的多个进程)“挂接”到这份新开辟的内存空间上(挂接就是通过页表映射到这片物理空间)-让不同的进程看到了同

2022-12-04 17:08:51 962

原创 Linux操作系统~匿名管道和命名管道的使用及其原理分析

1.创建子进程的时候,PCB也是要自己有一份的,同样里面的files_struct也是要自己独立有一份的(这些都属于进程的数据结构,进程是具有独立性的)2.而通过文件描述符找到的struct_file则是不需要给子进程拷贝一份的,因为这个是属于文件的,和创建进程没有关系。3.调用write方法的时候,是系统调用,会先将数据放在文件的内核缓冲区(不是C语言层面的缓冲区),底层定期的将缓冲区中的内容写到磁盘中。

2022-12-02 20:52:36 923

原创 Linux操作系统~尝试自己制作并使用动静态库

之后给别人用的时候,库文件和头文件都给,这样库文件就可以封装起来(转换成二进制文件)了。如果我们没有打包成库,.h文件(include的时候要写正确路径,或者借用环境)和.cc文件是分离的,此时编译的时候需要把.cc文件也要带上,否则会报错。为什么要这么设计呢?静态库在运行的时候不需要找库,所以运行的时候直接./mytest就行了,在编译的时候需要指定。动态库在运行的时候需要找库,所以除了在编译期间告诉编译器库在哪里,库的真实名字:去掉lib前缀,去掉.a...,so...后缀,剩下的就是库名称!

2022-11-22 23:53:07 947

原创 Linux操作系统~带你理解文件系统与软硬链接

这两个是不一样的概念,C语言中的FILE内部有当前文件对应的文件描述符fd,同时还有C语言层面上的缓冲区的内容等信息。然后操作系统根据FILE里面的fd,在fd作为下标的指针数组里面找到的file对象,里面存放了文件相关的inode元的信息(文件的属性信息),和C语言的FILE是不一样的。因为IO相关函数与系统调用接口对应,并且库函数封装系统调用,所以本质上,访问文件都是通过fd访问的。所以C库当中的FILE结构体内部,必定封装了fd。

2022-11-19 12:30:00 952

原创 Linux操作系统~系统文件IO,什么是文件描述符fd?什么是vfs虚拟文件系统

我们的输入输出最终都是访问硬件,OS是操作系统的管理者我们使用的都是语言层面上的接口,所以的“语言”上的操作,都必须贯穿OS。然而操作系统不相信任何人,所以访问操作系统都是需要通过系统调用的接口的。因此几乎所有的语言fopen,fclose,fread,fwrite,fgets,fputs,fgetc,fputc等底层一定需要使用OS提供的系统调用上面的 fopen fclose fread fwrite 都是C标准库当中的函数,我们称之为库函数(libc)。而,

2022-11-17 13:06:38 1514

原创 Linux操作系统~进程替换,exec系列函数的使用

这六个函数(加上execvpe一共七个)实际上只是参数上的不同,最终都是调用execve,系统给我们提供的实际上只有这一个接口,其他接口都是在此基础上封装出来的。(通过man手册也可以发现execve在man手册的2区内,表示是系统调用)

2022-11-15 21:10:26 1287

原创 Linux操作系统~进程fork到wait到底怎么用?

用户调用waitpid让父进程等待子进程退出并变成僵尸状态,然后从进程的pcb中获取exit_code和signal,组合起来放到status中,父进程中的status和用户层的status也就都发生了变化,用户就可以看到进程的退出码和终止信号了。2.既然父进程要拿到子进程的执行结果,也就需要保证父进程在子进程之后退出,使用wait可以保证时序问题,即让子进程先退出,父进程后退出。(查看进程的退出码)3.子进程退出的时候会先进入僵尸状态,会造成内存泄漏的问题,需要通过父进程wait,释放子进程占用的资源。

2022-11-11 20:01:04 1462

原创 Linux操作系统~什么是虚拟地址?深度剖析进程地址空间

1.堆是堆,栈是栈,堆栈是栈2.经过验证,C/C++的程序地址空间就是如图所示,栈区是往低地址方向增长的。子进程中对val进行修改,会发生写时拷贝。从而拷贝一份数据到另外的地方,变量的值是不一样的,但是他们两个变量对应的地址却是一样的,说明这个地址不是真正的物理地址,而是虚拟地址。

2022-11-07 08:45:00 1438

原创 Linux操作系统~进程有哪些状态?

D状态是一种不可中断的睡眠状态。比如一个进程让磁盘你帮我写1G数据到磁盘里面去,磁盘开始忙了,进程就会进入D状态等待磁盘写入完成(因为这种等待状态是不允许被中断的,因为如果中断进程的等待,磁盘写入数据完成后,无法向原来的进程返回错误/正确信息,会导致一些问题)D状态就是深度睡眠,处于这种状态的进程不能被中断的(不可以被杀掉)进程在内核中某些不能被信号打断,例如对某些硬件设备进行操作时刻(等待磁盘Io,等待网络io等等)。进程处于D状态一般情况下很短暂,不应该被top或者ps看到。

2022-11-04 19:50:56 4511

原创 Linux操作系统~什么是进程,进程的内部结构是什么?fork如何创建子进程?

一个进程里面有进程代码数据文件+PCB,PCB用双向链表的方式链接起来(组织进程),操作系统以后找进程的时候直接找PCB就行,而不是去找进程的代码和数据。子进程已经创建好了,此时父子进程都会向下执行,都会return,所以也就有两个返回值了(fork那条语句,子进程也会执行一部分,就是最后返回的那部分,所以会有两个返回值)如果fork成功了,对于父进程就会返回子进程的PID,对于子进程就会返回0。5.进程是具有独立性的,就算父进程挂了(修改数据),也不会影响子进程(不影响子进程的数据);

2022-11-01 14:58:33 406

原创 C语言实现一个进度条小程序

最近学习了Linux的常用命令,还有vim的基本使用,想着在Linux下写个简答的小程序玩玩,顺便熟悉一下vim编辑器的使用。这里写一个进度条的小程序本来想直接循环输出100个符号,但是还想着加一个可以显示当前加载的进度条百分比的效果,于是想到用while循环,每次重新打印进度条的内容,但是如何做到在同一行重复打印呢?我查了下资料,发现使用回车符\r可以回到当前行的开始,还发现了换行和回车实际上是两个不同的概念。回车是回到当前行的开始;

2022-10-30 14:42:56 1641

原创 Linux~一些基本开发工具的使用(yum,vim,gcc,gdb,makefile)

yum就类似手机上的应用市场,一些特定的组织或者个人把Linux下相关的软件都放在了一个特定的服务器上,yum会自动在特定的服务器上找到对应的服务,并下载下来,同时yum还会把当前服务所需要的其他服务(软件/依赖关系)都下载到你的主机上。mytest.s -o mytest.o ,将编译以后的汇编文件,也就是.s文件,进行汇编操作,完成以后生成mytest.o文件(可重定向的目标文件,一个二进制文件,在windows下就是mytest.obj)后面的clean :后面是空,实际上就是表示没有依赖关系。

2022-10-28 14:08:04 2466 2

原创 Linux~粘滞位到底有什么用?文件的默认权限为什么是664?

我们知道,如果一个用户想要在一个目录下创建并删除文件,需要有对这个目录的w,也就是写权限。那么让我们来看一个场景:这个时候该怎么做呢?直接给w权限就可以了吗?答案是不可以的,如果只是给其他用户写权限,那么用户除了可以创建删除自己的文件以外,还可以删除不属于他的文件。为了解决这样的问题,粘滞位就应运而生了。目录设置一种权限(无法对文件设置),加上粘滞位以后,对于普通用户,如果具有对这个目录的写权限,在这个目录下只允许创建文件,不允许普通用户删除/移动/重命名不属于自己的文件。

2022-10-26 15:54:40 1016

原创 Linux文件信息中的rwx表示什么?Linux权限理解

我们在命令行输入ll查看文件详情信息的时候,在文件信息的最前面可以看到-rw-rw-r--,这些表示的是什么呢?实际上这是该文件对不同用户所开放的权限,下面我们就来聊一聊Linux中的文件权限相关的内容。首先我们要理解什么是权限,权限表示的是某件事情能不能被某个人所做。所以权限实际上是由两部分组成的,一个是人,一个是事物权限属性,Linux下的各种用户对应的就是不同的人,文件中的读,写,修改就是文件可以提供给不同用户的权限属性。

2022-10-25 11:00:32 3008

原创 Linux常用指令/命令总结

指令用于复制文件或目录,如同时指定两个以上的文件或目录,且最后的目的地是一个已经存在的目录, 则它会把前面指定的所有文件或目录复制到此目录中。3. Change Time:简写为ctime,表示文件的状态时间,当文件的状态被修改时,更新这个时间,例如文件的链接数,大小,权限,Blocks数。2. Modify Time:简写为mtime,表示文件内容的修改时间,当文件的数据内容被修改时,更新这个时间。的 午夜,对应中国的北京时间是早上8:00,中国的机器是从8:00开始算的)开始所经过的秒数,不考虑闰秒。

2022-10-23 16:37:42 707 1

原创 C++~类和对象总结(万字大总结,值得收藏)

我们不写,编译器默认生成的构造函数我们写的无参构造函数我们写的全缺省构造函数(全缺省最常用,因为使用的时候既可以传参数也可以不传参数)C++把类型分成内置类型(基本类型包括指针)和自定义类型。默认生成构造函数对于内置类型成员变量不做处理,对于自定义类型成员变量才会处理(会调用这个自定义类型成员的默认构造函数)

2022-10-21 16:43:56 2180 11

原创 LeetCode416-分割等和子集(经典01背包问题—动态规划)

本题要我们判断是否能将一个集合刚好分成两个总和相等的子集,首先我们可以算出总和,然后看能否被2整除如果不能被2整除,那必然无法分成两个总和相等的子集,直接返回false。如果能被2整除,我们就尝试能否找到一个集合,总和为sum/2,找到就返回true,找不到就返回false那怎么找呢,这里就可以将这个问题转化成一个01背包问题,首先01背包问题的数组dp[i][j]表示的是表示在0-i个物品中,往容量为j的背包,放价值最大的物品。在本题中,集合中的数据就可以看成是一个个的物品,物品的重量和价值都是数

2022-10-18 23:11:54 1047 1

原创 C++~继承总结

继承(inheritance)是面向对象中使代码可以复用的最重要的手段,继承让我们在保持原有类特性的基础上进行扩展,增加功能,这样产生新的类,叫做子类。继承的实现过程是一个从一般到特殊的过程,体现出了面向对象设计的层次结构。继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。以前我们接触的复用都是函数复用(+复用+=),继承是类设计层次的复用(student和teacher都有名称,将名称定义在Person类里面,让student和teacher继承person)。

2022-10-14 23:40:49 428

原创 C++~LeetCode216-组合总和 III(回溯)

接着我们要确定传入的参数,题目给出的n和k以外,我们还需要一个nowSum来表示当前结果集中的和是多少,startNum表示现在从第几个数开始往后访问,因为组合是无序的,这里startNum的目的就是为了防止重复的组合出现。我们把整个问题的求解看成一颗树,树上的每条路径都是问题的一个可能的解,接着我们就是用for循环遍历当前层的集合元素,也就是当前层的结点,首先从startNum这个数开始,加入结果集,然后递归的去子树中寻找合适的结果集,找完以后返回。该列表不能包含相同的组合两次,组合可以以任何顺序返回。

2022-10-12 17:03:02 139

原创 C++~模板-泛型编程(函数模板,类模板,模板特化的使用和原理)

函数模板和类模板不支持声明和定义放到两个文件中,会出现链接错误普通的函数和类的话是可以的模板不支持声明和定义分别放到xxx.h和xxx.cpp中,一般是要放到一个文件中。有些地方就会命名成xxx.hpp,寓意就是头文件和定义实现内容合并—起但是并不是必须是.hpp,.h也是可以的,最好是.hpp。

2022-10-09 13:17:11 654

Java实现简单的扑克牌游戏(无bug,能正常运行)

Java实现简单的扑克牌游戏,思路及其清晰,十分适合初学者学习与应用。有洗牌,发牌等基本功能,还可以玩捉鬼游戏,附带详细讲解。

2021-12-13

Java实现简单的图书管理系统

java实现了简易的图书管理系统,有管理员和普通用户两种用户,管理员能查找图书,删除图书,新增图书,显示所有图书等功能。用户有查找图书,借阅图书,归还图书等功能。总共分了User,Operation,Book三个模块,代码都有详细注释

2021-11-23

空空如也

TA创建的收藏夹 TA关注的收藏夹

TA关注的人

提示
确定要删除当前文章?
取消 删除