当前位置: 首页 > news >正文

自行编写一个简单的shell!

本文旨在编写一个简单的shell外壳程序!功能类似于shell的一些基本操作!虽然不能全部实现shell的一些功能!但是通过此文章,自己写一个简单的shell程序也是不成问题!并且通过此文章,可以让读者对linux中一些环境变量等基本概念有更深的理解!希望读完本篇文章能对读者有一定的收获!文末会附带自己编写shell的源码!


好的废话少说,正文开始!

首先我们先来看一下linux中的shell长什么样子!

这是其shell刚启动的时候的样子!其外貌就是一个中括号内部加上一系列的东西!其实当我们认真观察,不难发现,里面包括的就是“用户名“+“@”+“主机名字”+“当前工作路径!”那么发现了此规律之后我们不难实现此描述框!那么接下来我们就着手与这些描述框的实现!

linux描述框的实现!

其中要想获得我们的用户名!我们其实可以通过环境变量进行获取,那么该如何获取环境变量的值呢?这里就不得不引进一个获得环境变量的值的函数了!

getenv(“USER”)

通过查询man手册,我们可以发现getenv()函数只需要传递一个参数即可!那么此参数是什么呢?其实此参数就是我们想要获得环境变量的值的名字!所以要想获得用户名,我们可以直接使用getenv(USER),即可获得我们想要的用户名!我们可以验证一下USER对应的环境变量是否真的是我们所要的环境变量名!我们可以通过echo $USER  此命令来判断是否真的是我们想要的用户名!

不难看出,USER对应的环境变量确实是我们的用户名!


getenv(“HOSTNAME”)

既然有了用户名,那么我们的主机名如何获得呢?思路还是调用getenv(HOSTNAME)操作!获取主机名!同样的也可以通过echo命令进行验证!这里就不再累赘了!


getenv(“PWD”)

最后再来获取我们的当前工作目录!也是调用getenv函数!同样的可以通过echo命令进行验证!


那么这些基本的环境变量都出来了,我们是否可以通过上述思路来创建一个简单的描述框呢?

代码如下:

其中这里为了方便起见,直接将各个函数进行封装!保证代码的健壮性!

其中还需要扩充的几点有:

为了区别与系统的shell,我们在描述框后面加上一个#以区分系统的$ !这样我们的描述框已经基本实现了!

获取用户指令以及将其分割!

那么基本的描述框已经实现了!我们还需要做的一点就是获取用户输入的指令!那么如何获取用户的指令呢?思路很简单:定义一个数组,然后将用户输入的字符放到数组中即可!!那么能否用scanf函数呢?答案是肯定不行!因为用户输入的指令一般都是指令+选项!其中指令和选项之间都是有着空格来间隔区分的!那么应该如何获取用户的输入呢?答案很简单,用fgets函数即可,那么接下来我们就来介绍一下fgets函数的用法!!

fgets函数

 通过查询man手册可以看出,其中fgets函数中有三个参数,第一个是就是缓冲区即(将要被写到哪里的地址!)第二个参数表示此缓冲区的大小!第三个参数是用哪些流进行写入!一般第三个参数我们都选择(stdin标准输入流)进行写入!

既然介绍了fgets函数的用法,那么我们就知道我们需要创建一个数组来存放即将要写入的数据!数组的大小自己来定义即可!

那么用户的指令获取成功之后,我们需要将用户的指令进行打散然后利用execvp进行替换即可!那么如何进行打散这段字符串呢?这里就不得不引进我们C语言中的strtok函数了!

strtok()函数!

查询man手册可以得知,strtok有两个参数!其中第一个参数是将要打散的原字符串,第二个字符串指的是用于打散的标记符都有哪些。

返回值:第一次调用,返回标记符第一次出现的位置,然后并将标记符转化为\0,此时会记住此位置!然后再次使用的使用第一个参数只需要传NULL指针即可!如果最终不可再进行分割的时候,返回值就会返回NULL!这样就可以将原字符串进行打散!我们的目的是想要将其打散放在一个数组中,方便之后使用!所以我们还需要自己再定义一个指针数组用于存放分割后的各个字符串!

通过以上的思路,我们就可以将用户命令和将命令打散此功能进行实现了!

代码如下:

其中第60行是将最后的\n转化为\0,防止其进行跳行!!其中在commandSplit函数中,我们还设置了条件宏!用于检查我们的代码是否将原字符串进行正确的打断!如果最后不想要打印出分割后的字符串,可以将宏定义取消即可!其中char*out[]表示打散后的数组!!char *in 表示的是原字符串!spint是一个宏定义用来标明分割字符串都有哪些,这里分割符只要空格!

至此,描述框和获取用户指令都已经实现了!

完成进程替换!

那么我们如何将用户的指令转化为shell的操作呢?这里就得引进进程替换的概念!我们需要将进程进行替换来让他执行我们想让他执行的代码!

那么进程替换有很多中调用方式?我们应该选择那种呢?其实很简单!我们已经将用户的命令行进行打断分散处理了,所以我们完全可以根据v的特性来进行选择,又因为我们并不知道用户以后需要输入的指令,所以我们也不知道其指令所在路径,所以我们就可以使用execvp这个系统调用来进行进程替换!其中v我们已经有了!p默认为我们提供了路径,所以用户的指令肯定是存放在v[0]上的!所以我们的进程替换就可以写出来了!

需要注意的是,我们要进行进程替换的时候,一定不要让我们的父进程进行替换!因为一旦父进程进行替换的时候,如果进程挂掉了,那么我们的shell不就是结束了么,所以我们可以使用fork来创建子进程来进行进程替换,而父进程只需要等待子进程退出,回收其资源即可!

下面来看一下进程替换的代码!

至此,进程替换的指令也可以实现了,我们自定义的shell程序也能实现ls  top pwd 等操作了!但是对于其他命令我们自定义的shell程序却不能正确的执行了!例如cd命令,还有export命令!这是为什么呢?这就不得不引进内建命令了!

内建命令

何为内建命令呢?内建命令就是这些命令只能由bash自己执行!而不能让子进程进行执行!那么我们常见的linux中有哪些命令是内建命令呢,下面就来简单的介绍几个内建命令,并且在我们自定义的shell中实现这些内建命令!!

cd命令!!

其中cd是一种常见的内建命令!这个指令只能交付给父进程自己执行,而不能交付给子进程让子进程执行!因为cd指的就是改变当前的路径,如果交给子进程进行执行,那么父进程的路径将不会修改!那么该如何进行编写我们shell中的cd命令呢?

代码如下:

其中cd主要进行的操作就是将当前的工作目录进行修改!那么如何修改当前的工作目录呢?这里就不得不引进chdir这个系统调用了!

chdir()

其中chdir函数只有一个参数,这个参数代表的是将要修改的路径!我们只需要定义一个字符数组,然后将我们要修改的路径存放到此数组中,然后将此数组就进行传递即可完成改变当前的路径!其中还需要将当前的环境变量PWD也进行修改!创建一个临时数组和全局数组,全局用于存放环境变量的值!然后将修改后的环境变量的值写入到全局数组中!最后再将环境变量进行同步!只需要调用putenv就可以将环境变量进行修改!

export命令!

还有一个常见的内建命令就是export,那么什么是export呢?export命令就是将我们定义的变量导入到环境变量之中!下面来看一下如何实现我们自己shell的export命令!

代码如下:

其中我们需要定义一个全局变量的数组用于存放我们的环境变量的值!如果我们使用的是局部变量的话!就会导致每次用户输入命令的时候,我们不更新环境变量,其环境变量就会自动消失!这是因为局部变量的局部性!所以定义一个全局变量是最为合适的!但是此代码也有一个小bug,就是当再导入一个新的环境变量的时候,之前的那个环境变量就会消失!

既然环境变量也能导出了,那么我们总得知道是否真正的将其导出了,这里就得引出了echo命令了,因为此命令也是内建命令,所以也得交给我们的父进程自己执行!下面就来写一下关于echo命令的代码!

ehco命令!

其中echo命令简单分为三个功能!第一个是回显出退出码!第二个是显示出环境变量的值!第三个就是普通的回显字符串!

对于第一个回显错误码:我们只需要判断其分割后的第二个字符串是否是“$”即可!然后根据$后面跟的字符即可判断出来,如果$后面跟的是"?"字符的话,那就是显示出退出码的信息!如果“$”后面跟的不是"?"而是一个字符串!那么就是显示出其环境变量的值!最后如果连"$"字符都没有的话,那就是简单的回显字符串了!

这就是echo命令实现的简单逻辑了!

但是我们写的shell还有大多数功能没有实现,比如本地变量的存储,以及重定向的操作!对于本地变量的存储,我们可以用malloc在堆内申请空间来存储变量中的值,对于重定向!我们可以利用dup函数进行重定向的操作!下面来看一下简单的检查是否有重定向的函数吧!

重定向:

其中SkipSpace是一个宏,其作用就是跳过空格的!我们检查是否有重定向,顺便也能将文件名与命令相分割,然后在execu函数体内进行重定向的操作!

这样我们的shell也能支持重定向的功能了!

至此我们自己的shell已经初步完成了,它能完成一些简单的操作!!希望读完本文,读者也尝试写一下shell的实现!

下面将源码附在下面!

源码

#include<stdio.h>
#include<string.h>
#include<sys/types.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<sys/wait.h>
#include<unistd.h>
#include<stdlib.h>
#define Size 50
#define NUM 1024
#define spint " "
//#define debug 1#define NOredir 0
#define AppendRedir 3
#define InputRedir  1
#define OutputRedir 2#define SkipSpace(pos) do{ while(isspace(*pos)) pos++; }while(0)char *filename=NULL;
int redir=NOredir;
int lastcode=0;
char enval[1024];
char cwd[1024];
// char eni[1024];
const char* getUser()
{char* user=getenv("USER");if(user){return user;}else{return "none";}}const char*getHost()
{char *host=getenv("HOSTNAME");if(host){return host;}else{return "none";}
}char*gethome()
{char *pwd=getenv("PWD");if(pwd){return pwd;}else{return "none";}
}int getcommand(char*command,int n)
{printf("[%s@%s %s]#",getUser(),getHost(),gethome());char*r=fgets(command,n,stdin);if(r==NULL) return 0 ;command[strlen(command)-1]='\0';return 1;
}void commandSplit(char *in,char *out[])
{int argc=0;out[argc++] =strtok(in,spint);while(out[argc++]=strtok(NULL,spint));
#ifdef debug int i=0;for(i=0;out[i];i++){// printf("%d:%s\n",i,out[i]);printf("%s\n",out[i]);}// printf("\n");
#endif
}//只需要将用户的命令行数组指令传递过来即可!
int execute(char* argv[])
{pid_t rit=fork();if(rit==0){int fd=0;if(redir==InputRedir){fd = open(filename, O_RDONLY); // 差错处理我们不做了dup2(fd, 0);}else if(redir==OutputRedir){fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666);dup2(fd, 1);}else if(redir==AppendRedir){fd = open(filename, O_WRONLY | O_CREAT | O_APPEND, 0666);dup2(fd, 1);}else{//do nothing}//子进程!用于进程切换!而不是让父进程bash直接自己运行!//其中进程替换直接用execvp函数即可,因为我们有了用户的命令行了!execvp(argv[0],argv);exit(0);//如果替换失败就会退出!负责代表进程替换成功!}else{int status=0;//父进程!只需要等待子进程退出即可!pid_t ret=waitpid(rit,&status,0);if(ret==rit){// printf("wait success\n");lastcode = WEXITSTATUS(status);// printf("%d",lastcode);//  return 0;}}return 0;
}void cd(const char*path)
{chdir(path);char tem[1024];getcwd(tem,sizeof(tem));sprintf(cwd,"PWD=%s",tem);putenv(cwd);}
//检查是否为内建命令并执行!
int dobuildin(char*argv[])
{//cd命令!if(strcmp(argv[0],"cd")==0){char *path=NULL;if(argv[1]==NULL)  {path=gethome(); }else path=argv[1];cd(path);return 1;}else if(strcmp(argv[0],"export")==0){ if(argv[1]==NULL) return 1; strcpy(enval,argv[1]);//  strcpy(envir,argv[1]);// putenv(envir);//此处需要用全局变量数组来存储env 因为一旦使用局部变量的时候,会随着用户输入的指令putenv(enval);//此处需要用全局变量数组来存储env 因为一旦使用局部变量的时候,会随着用户输入的指令//环境变量会消失!return 1;}else if(strcmp(argv[0],"echo")==0){//与系统中的echo保持一致!if(argv[1]==NULL){printf("\n");return 1;}if(*(argv[1])=='$'&&strlen(argv[1])>1){char *val=argv[1]+1;if(strcmp(val,"?")==0){printf("%d\n",lastcode);lastcode=0;}else{char *enval=getenv(val);if(enval) printf("%s\n",enval);else{printf("\n");}// return 1;}return 1;}else{printf("%s\n",argv[1]);return 1;}// return 1;}else if(0){}return 0;
}void checkRedir(char usercommand[], int len)
{// ls -a -l > log.txt// ls -a -l >> log.txtchar *end = usercommand + len - 1;char *start = usercommand;while(end>start){if((*end) == '>'){if(*(end-1) == '>'){*(end-1) = '\0';filename = end+1;SkipSpace(filename);redir = AppendRedir;break;}else{*end = '\0';filename = end+1;SkipSpace(filename);redir = OutputRedir;break;}}else if(*end == '<'){*end = '\0';filename = end+1;SkipSpace(filename); // 如果有空格,就跳过redir = InputRedir;break;}else{end--;}}
}int main()
{while(1){char userCommand[NUM];char* argv[Size];//显示框架!获取用户输入的指令!int n= getcommand(userCommand,sizeof(userCommand));// if(n==0) continue;// printf("%s")//将用户的命令进行切割!checkRedir(userCommand,strlen(userCommand));commandSplit(userCommand,argv);//判断命令是否为内建命令1!int k=dobuildin(argv);if(k) continue;//创建子进程用于进行进程替换!execute(argv);}// return 0;
}

相关文章:

自行编写一个简单的shell!

本文旨在编写一个简单的shell外壳程序&#xff01;功能类似于shell的一些基本操作&#xff01;虽然不能全部实现shell的一些功能&#xff01;但是通过此文章&#xff0c;自己写一个简单的shell程序也是不成问题&#xff01;并且通过此文章&#xff0c;可以让读者对linux中一些环…...

mvn site 命令

概述 在Maven中&#xff0c;site指的是一个特定的阶段&#xff0c;其目的是生成项目相关的站点文档。这些站点文档可以为项目的开发者、用户、以及其他利益相关者提供有关项目的详细信息。 Maven的站点文档通常包括以下内容&#xff1a; 项目信息&#xff1a;这部分提供了关于…...

<JavaEE> 经典设计模式之 -- 定时器

目录 一、定时器的概念 二、Java 标准库中的定时器 三、实现自己的定时器 一、定时器的概念 什么是定时器&#xff1f;定时器是软件开发中的一个常用且重要组件&#xff0c;作用是在达到设定时间后&#xff0c;执行指定的代码。 二、Java 标准库中的定时器 1&#xff09;T…...

【C++ Primer Plus学习记录】if语句

目录 一、if语句 二、if else语句 三、格式化if else语句 四、if else if else结构 一、if语句 if语句让程序能够决定是否应执行特定的语句。 if有两种格式&#xff1a;if和if else。 if语句的语法与while相似&#xff1a; if(test-condition)statement; 如果test-con…...

结构体,自定义类型

目录 结构体 结构体的声明 结构体的自引用 结构体的定义和初始化 结构体内存对齐 ​编辑 结构体的对齐规则&#xff1a; 为什么存在内存对齐&#xff1f; 修改默认对齐数 结构体传参 位段 什么是位段 位段的内存分配 位段的跨平台问题 枚举 联合&#xff08;共用体…...

Ubuntu22.04通过Maas和Juju部署openstack charm

目录 官方文档材料准备软件硬件 模板机和虚拟网络安装MAAS官方文档MAAS节点配置安装MAAS浏览器登录MAAS进行配置 激活DHCP 官方文档 https://docs.openstack.org/project-deploy-guide/charm-deployment-guide/2023.1/ 这是一个通过Maas面板即可部署openstack的方式&#xff0…...

老有所依:TSINGSEE青犀养老院智能视频监管方案

养老院智能监控方案是为了提高养老院内老人的安全和护理质量&#xff0c;利用智能技术与监控设备进行全方位的监控和管理&#xff0c;可以加强对老人的监护和护理&#xff0c;提高养老院的服务质量和安全性。 旭帆科技基于视频技术与AI智能分析技术构建的养老院智能视频监控方…...

vue中的this.$nextTick().then()

MENU 示例一示例二sortsplicepushrandomfloorMathwhile演示 示例一 let reorganize function (arr){let rest [];while (arr.length > 0) {let random Math.floor(Math.random() * arr.length);// 把获取到的值放到新定义的数组中rest.push(arr[random]);// 这句代码的作…...

Python处理Excel文件并与数据库匹配做拼接

Python处理Excel文件并与数据库匹配做拼接 需求&#xff1a;Python处理Excel中数据并于数据库交互匹配得到账号信息等其他操作 Python实现 import os import pandas as pd import pymssql import warnings import time# 提取速率函数 def extract_broadband_speed(speed):if…...

【出现模块node_modules里面包找不到】

#pic_center R 1 R_1 R1​ R 2 R^2 R2 目录 一、出现的问题二、解决办法三、其它可供参考 一、出现的问题 在本地运行 npm run docs:dev之后&#xff0c;出现 Error [ERR_MODULE_NOT_FOUND]: Cannot find package Z:\Blog\docs\node_modules\htmlparser2\ imported from Z:\Blo…...

高项备考葵花宝典-项目进度管理输入、输出、工具和技术(中,很详细考试必过)

项目进度管理的目标是使项目按时完成。有效的进度管理是项目管理成功的关键之一&#xff0c;进度问题在项目生命周期内引起的冲突最多。 小型项目中&#xff0c;定义活动、排列活动顺序、估算活动持续时间及制定进度模型形成进度计划等过程的联系非常密切&#xff0c;可以视为一…...

sql注入 [GXYCTF2019]BabySQli1

打开题目 多次尝试以后我们发现存在一个admin的账号&#xff0c;但是密码我们不知道 我们尝试一下万能密码 admin or 11 -- q 报错 我们尝试bp抓一下包看看 看着很像编码 先去base32解码 再base64解码 得到 我们从这个sql语句中得到注入点为name 根据报错信息我们知道是…...

python二维数组创建赋值问题:更改单个值却更改了所有项的值

test_list [] dic1 {} test_list [dic1 for _ in range(3)] ll [1, 2, 3]for i in range(3):test_list[i][value] ll[i]print(test_list)运行结果&#xff1a;每次赋值都更改了所有项 原因&#xff1a;python的二位数据创建方式就是这样&#xff0c;官方文档中有描述Wha…...

深度模型训练时CPU或GPU的使用model.to(device)

一、使用device控制使用CPU还是GPU device torch.device("cuda:0" if torch.cuda.is_available() else "cpu") # 单GPU或者CPU.先判断机器上是否存在GPU&#xff0c;没有则使用CPU训练 model model.to(device) data data.to(device)#或者在确定有GPU的…...

SpringBoot3-实现和注册拦截器

1、pom.xml <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.apache.org/POM/4.0.…...

Ubuntu 22.04源码安装yasm 1.3.0

sudo lsb_release -r看到操作系统的版本是22.04&#xff0c;sudo uname -r可以看到内核版本是5.15.0-86-generic&#xff0c;sudo gcc --version可以看到版本是11.2.0&#xff0c;sudo make --version可以看到版本是GNU Make 4.3。 下载yasm http://yasm.tortall.net/Downlo…...

LeetCode [中等]矩阵置零

73. 矩阵置零 - 力扣&#xff08;LeetCode&#xff09; 暴力解法 用两个标记数组分别记录每一行和每一列是否有零出现。 遍历该数组一次&#xff0c;如果某个元素为 0&#xff0c;那么就将该元素所在的行和列所对应标记数组的位置置为 true。再次遍历该数组&#xff0c;用标…...

十一、了解分布式计算

1、什么是&#xff08;数据&#xff09;计算&#xff1f; 2、分布式(数据)计算 &#xff08;1&#xff09;概念 顾名思义&#xff0c;分布式计算&#xff0c;即以分布式的形式完成数据的统计&#xff0c;得到需要的结果。 分布式数据计算&#xff0c;顾名思义&#xff0c;就是…...

数据结构和算法专题---2、算法思想

上文讲到算法的概念、复杂度&#xff0c;本文给大家介绍具体的算法思想&#xff0c;让大家对算法设计理念有个认识&#xff0c;后续再分别介绍各种算法。 算法思想 算法是解决问题的一种思想和方法&#xff0c;其基本思想是将一个复杂问题分解为多个简单的子问题&#xff0c;…...

在AWS Lambda上部署标准FFmpeg工具——自定义层的方案

大纲 1 确定Lambda运行时环境1.1 Lambda系统、镜像、内核版本1.2 运行时1.2.1 Python1.2.2 Java 2 打包FFmpeg3 创建Lambda的Layer4 测试4.1 创建Lambda函数4.2 附加FFmpeg层4.3 添加测试代码4.4 运行测试 参考文献 FFmpeg被广泛应用于音/视频流处理领域。对于简单的需求&#…...

变量 varablie 声明- Rust 变量 let mut 声明与 C/C++ 变量声明对比分析

一、变量声明设计&#xff1a;let 与 mut 的哲学解析 Rust 采用 let 声明变量并通过 mut 显式标记可变性&#xff0c;这种设计体现了语言的核心哲学。以下是深度解析&#xff1a; 1.1 设计理念剖析 安全优先原则&#xff1a;默认不可变强制开发者明确声明意图 let x 5; …...

ardupilot 开发环境eclipse 中import 缺少C++

目录 文章目录 目录摘要1.修复过程摘要 本节主要解决ardupilot 开发环境eclipse 中import 缺少C++,无法导入ardupilot代码,会引起查看不方便的问题。如下图所示 1.修复过程 0.安装ubuntu 软件中自带的eclipse 1.打开eclipse—Help—install new software 2.在 Work with中…...

【开发技术】.Net使用FFmpeg视频特定帧上绘制内容

目录 一、目的 二、解决方案 2.1 什么是FFmpeg 2.2 FFmpeg主要功能 2.3 使用Xabe.FFmpeg调用FFmpeg功能 2.4 使用 FFmpeg 的 drawbox 滤镜来绘制 ROI 三、总结 一、目的 当前市场上有很多目标检测智能识别的相关算法&#xff0c;当前调用一个医疗行业的AI识别算法后返回…...

Mysql中select查询语句的执行过程

目录 1、介绍 1.1、组件介绍 1.2、Sql执行顺序 2、执行流程 2.1. 连接与认证 2.2. 查询缓存 2.3. 语法解析&#xff08;Parser&#xff09; 2.4、执行sql 1. 预处理&#xff08;Preprocessor&#xff09; 2. 查询优化器&#xff08;Optimizer&#xff09; 3. 执行器…...

【笔记】WSL 中 Rust 安装与测试完整记录

#工作记录 WSL 中 Rust 安装与测试完整记录 1. 运行环境 系统&#xff1a;Ubuntu 24.04 LTS (WSL2)架构&#xff1a;x86_64 (GNU/Linux)Rust 版本&#xff1a;rustc 1.87.0 (2025-05-09)Cargo 版本&#xff1a;cargo 1.87.0 (2025-05-06) 2. 安装 Rust 2.1 使用 Rust 官方安…...

GO协程(Goroutine)问题总结

在使用Go语言来编写代码时&#xff0c;遇到的一些问题总结一下 [参考文档]&#xff1a;https://www.topgoer.com/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/goroutine.html 1. main()函数默认的Goroutine 场景再现&#xff1a; 今天在看到这个教程的时候&#xff0c;在自己的电…...

深度学习之模型压缩三驾马车:模型剪枝、模型量化、知识蒸馏

一、引言 在深度学习中&#xff0c;我们训练出的神经网络往往非常庞大&#xff08;比如像 ResNet、YOLOv8、Vision Transformer&#xff09;&#xff0c;虽然精度很高&#xff0c;但“太重”了&#xff0c;运行起来很慢&#xff0c;占用内存大&#xff0c;不适合部署到手机、摄…...

DeepSeek源码深度解析 × 华为仓颉语言编程精粹——从MoE架构到全场景开发生态

前言 在人工智能技术飞速发展的今天&#xff0c;深度学习与大模型技术已成为推动行业变革的核心驱动力&#xff0c;而高效、灵活的开发工具与编程语言则为技术创新提供了重要支撑。本书以两大前沿技术领域为核心&#xff0c;系统性地呈现了两部深度技术著作的精华&#xff1a;…...

图解JavaScript原型:原型链及其分析 | JavaScript图解

​​ 忽略该图的细节&#xff08;如内存地址值没有用二进制&#xff09; 以下是对该图进一步的理解和总结 1. JS 对象概念的辨析 对象是什么&#xff1a;保存在堆中一块区域&#xff0c;同时在栈中有一块区域保存其在堆中的地址&#xff08;也就是我们通常说的该变量指向谁&…...

CTF show 数学不及格

拿到题目先查一下壳&#xff0c;看一下信息 发现是一个ELF文件&#xff0c;64位的 ​ 用IDA Pro 64 打开这个文件 ​ 然后点击F5进行伪代码转换 可以看到有五个if判断&#xff0c;第一个argc ! 5这个判断并没有起太大作用&#xff0c;主要是下面四个if判断 ​ 根据题目…...