c/c++数据类型转换.
author: hjjdebug
date: 2025年 05月 18日 星期日 20:28:52 CST
descrip: c/c++数据类型转换.
文章目录
- 1. 为什么需要类型转换?
- 1.1 发生的时机:
- 1.2 常见的发生转换的类型:
- 2. c语言的类型转换: (Type) value
- 2.1 c语言的类型变换是如何实现的? 规则是什么?
- 3. c++ 的static_cast
- 4. c++的reinterpret_cast
- 5. dynymic_cast 的使用.
- 5.1: 测试代码
- 5.2 statci_cast 处理父子类对象地址
- 5.3 动态变换: dynamic_cast 处理父子类对象地址
- 5.4 为什么指针类型老是变来变去的,是什么类型就是什么类型,不变不好吗?
- 5.4 为什么要用 dynamic_cast ?
本来数据是什么类型就是什么类型,一般是不需要转换的,
但某些特殊情况下会发生转换.
1. 为什么需要类型转换?
因为类型不匹配,所以要进行类型转换.
1.1 发生的时机:
- 当进行赋值和运算时有可能发生数据转换.
- 进行函数调用时,有可能会发生数据类型转换.
有可能是说类型不匹配时,会按照一定规则进行变换,如果不是自定义的规则,
那就是编译器隐含的规则.
1.2 常见的发生转换的类型:
- 整形变成浮点型,浮点型变成整形.
- 子类指针退化为父类(上变换)和父类指针变换为子类(下变换)
- 自定义的类型变换规则.
2. c语言的类型转换: (Type) value
举例:
$ cat main.cpp
#include <stdio.h>
int main()
{int i = 1;double d = i; // 隐式类型转换printf("i:%d, d:%.2f\n",i,d);int* ptr = &i;long address = (long)ptr; // 显示的强制类型转换printf("ptr:%p, address:%ld\n",ptr,address);return 0;
}
执行:
$ ./Test
i:1, d:1.00
ptr:0x7ffca9a5b0bc, addr:140723154694332,addr:0x7ffca9a5b0bc
2.1 c语言的类型变换是如何实现的? 规则是什么?
4 int i = 1;
0x00005555555546c1 <+23>: movl $0x1,-0x24(%rbp) //把1赋值给变量i
5 double d = i; // 隐式类型转换
0x00005555555546c8 <+30>: mov -0x24(%rbp),%eax //取到数值i
0x00005555555546cb <+33>: cvtsi2sd %eax,%xmm0 //变成浮点数
0x00005555555546cf <+37>: movsd %xmm0,-0x20(%rbp) //保存数值到d变量
7 int* ptr = &i;
0x00005555555546f7 <+77>: lea -0x24(%rbp),%rax //取到i的地址
0x00005555555546fb <+81>: mov %rax,-0x18(%rbp) //把地址保存到ptr变量
8 long address = (long)ptr; // 显示的强制类型转换
0x00005555555546ff <+85>: mov -0x18(%rbp),%rax //从ptr地址中取到地址(解引用的意思)
0x0000555555554703 <+89>: mov %rax,-0x10(%rbp) //把地址保存到address 变量
可见地址类型就天然等于long 类型, 它们数值时完全相等的.
c语言会按照它默认规则进行转换,例如把long 转为 int 它也给转,当正确性你自己保证
c++认为c的强制类型转换(type)value 方式太过强暴, 将之分解为4重转换类型,
static_cast, 静态转换
dynymic_cast, 动态转换
reinterpreted_cast, 再解释转换
const_cast, 常变换.
3. c++ 的static_cast
测试代码:
#include <iostream>
using namespace std;
int main()
{double d = 1.23;int a = static_cast<int>(d);cout << a << endl;int* p = &a;
// int address = static_cast <int>(p); //error: invalid static_cast from type ‘int*’ to type ‘int’
// long address = static_cast <long>(p); //error: invalid static_cast from type ‘int*’ to type ‘long int’long address = reinterpret_cast <long>(p); //error: invalid static_cast from type ‘int*’ to type ‘long int’cout << address << endl;return 0;
}
我们看到static_cast 把浮点数到int的变换与c下的转换时完全一致的.
汇编代码如下:
5 double d = 1.23;
=> 0x0000555555554892 <+8>: movsd 0x136(%rip),%xmm0 # 0x5555555549d0
0x000055555555489a <+16>: movsd %xmm0,-0x8(%rbp)
6 int a = static_cast(d);
0x000055555555489f <+21>: movsd -0x8(%rbp),%xmm0
0x00005555555548a4 <+26>: cvttsd2si %xmm0,%eax
0x00005555555548a8 <+30>: mov %eax,-0xc(%rbp)
但它拒绝将整形地址向整形变量转换,也拒绝向long 整形变量转换. 编译会直接给出错误.
可见对于static_cast, 它的转换实现与c下的强制转换是一样的.
但对于高风险的转换,它不干了.
如果你确实需要把指针变成整数怎么办,那你可以用另一个关键字reinterpret_cast, 它的要求
会放宽,允许你转换,不过后果自负喽!, 你自己负责使用时的正确性.
4. c++的reinterpret_cast
从上面的例子可以看出,static_cast 对风险高的转换它不干了,让reinterpret_cast来承担风险. reinterpret_cast 的转换方式跟c下的强制转换是一样的.
5. dynymic_cast 的使用.
其主要是为了完成指针类型的上变换(辈份变大了和下变换辈分变小了)
也可能辈分没有变,但从一个基类指针变到了另一个基类指针.
c++的类和继承关系, 一个形象的理解是一个类就对应一个椭圆,子类继承了父类,那就是一个大椭圆套住了一个小椭圆,
继承了2个基类,那就是套住了2个小椭圆.
static_cast 可以处理这种有继承关系的对象指针的转换.
dynymic_cast 也可以处理这种有继承关系的对象指针的转换. 但它是动态的,支持运行时监测.
完成这种转换是需要类型信息的, 下面举例说明转换是怎样进行的,
5.1: 测试代码
$ cat main.cpp
#include <iostream>
using namespace std;
// 假设有2个基类 Base1和Base2 和一个派生类 Derived,各类型指针转换会是什么?
class Base1
{public:// error: cannot dynamic_cast ‘p’ (of type ‘class Base*’) to type ‘class Derived*’ (source type is not polymorphic)// 必需要有一个虚函数,否则编译错误说类型不具有多态性.virtual void foo() {cout<<"from Base1::foo\n";}
};
class Base2
{public:virtual void bar() {cout<<"from Base2::bar\n";}
};class Derived : public Base1, public Base2
{public:void compose() { cout<< "from Derived:compose\n";}
};
int main()
{Derived * pd = new Derived();// 用基类指针,指向一个派生类对象Base1* p1 = pd;Base2* p2 = pd;//当你看到p1,p2的不同,不要奇怪,这里有隐含指针变换cout<<"p1:"<<p1<<",p2:"<<p2<<",pd:"<<pd<<endl;// 使用 dynamic_cast 将 p1 转换为派生类指针 q, 下变换// 能变换吗? 能,因为p1 本来就是派生类对象的指针.// 如果p1 是用 new Base() 创建出来的, 那就转不成继承类指针了!// 转不成了变换的值是nullptr.// dynamic_cast<Derived *> 可看成是一个编译器生成的内置函数// 变化比较复杂. 有对指针的调整. 需要RTTI 运行时类型信息支持.Derived* q1 = dynamic_cast<Derived*>(p1);cout<<"p1:"<<p1<<",q1:"<<q1<<endl; //p1 和q1 是相等的.Derived* q2 = dynamic_cast<Derived*>(p2);//当你看到转出的q1,q2竟然相等,也不用奇怪,因为它们就是一个对象cout<<"q1:"<<q1<<",q2:"<<q2<<",p1:"<<p1<<",p2"<<p2<<endl;// 甚至从Base1 也能导出Base2指针,都是一个对象,它们是可以互相推导计算的.// 推导计算的法则是根据RTTI信息,计算就是加一个偏移,减一个偏移的事情.// 如果转换成功,q1 不为空,可以调用派生类的成员函数 compose()if (q1){q1->compose();}else{cout << "down convert failed\n";}return 0;
}
5.2 statci_cast 处理父子类对象地址
继承类指针向基类指针的隐含转换.
24 // 用基类指针,指向一个派生类对象
25 Base1* p1 = pd;
0x00000000004008f8 <+49>: mov -0x38(%rbp),%rax
0x00000000004008fc <+53>: mov %rax,-0x30(%rbp) //直接赋值
26 Base2* p2 = pd;
0x0000000000400900 <+57>: cmpq $0x0,-0x38(%rbp) //pd==0? 付给0
0x0000000000400905 <+62>: je 0x400911 <main()+74>
0x0000000000400907 <+64>: mov -0x38(%rbp),%rax
0x000000000040090b <+68>: add $0x8,%rax //把值加上8,付给p2
0x000000000040090f <+72>: jmp 0x400916 <main()+79>
0x0000000000400911 <+74>: mov $0x0,%eax
0x0000000000400916 <+79>: mov %rax,-0x28(%rbp)
36 Derived* q1 = static_cast<Derived*>(p1); //静态变换,也叫编译期变换
0x0000000000400999 <+210>: mov -0x30(%rbp),%rax
0x000000000040099d <+214>: mov %rax,-0x20(%rbp) // p1直接付给了q1
// p2付q2, 它就改一改算法, 你说gcc多聪明啊! 它了解意图.
38 Derived* q2 = static_cast<Derived*>(p2);
0x00000000004009ff <+312>: cmpq $0x0,-0x28(%rbp) // p2==0? 付给0
0x0000000000400a04 <+317>: je 0x400a10 <main()+329>
0x0000000000400a06 <+319>: mov -0x28(%rbp),%rax
0x0000000000400a0a <+323>: sub $0x8,%rax // p2减去8,付给q2
0x0000000000400a0e <+327>: jmp 0x400a15 <main()+334>
0x0000000000400a10 <+329>: mov $0x0,%eax
0x0000000000400a15 <+334>: mov %rax,-0x18(%rbp)
5.3 动态变换: dynamic_cast 处理父子类对象地址
36 Derived* q1 = dynamic_cast<Derived*>(p1);
0x00000000004009e9 <+210>: mov -0x30(%rbp),%rax
0x00000000004009ed <+214>: test %rax,%rax
0x00000000004009f0 <+217>: je 0x400a0f <main()+248> //p1==0? 付给0
0x00000000004009f2 <+219>: mov $0x0,%ecx //第4参数,0,偏移数据
0x00000000004009f7 <+224>: mov $0x601da0,%rdx //第3参数,类型信息
0x00000000004009fe <+231>: mov $0x601de8,%rsi //第2参数,类型信息
0x0000000000400a05 <+238>: mov %rax,%rdi // 第一参数p1
0x0000000000400a08 <+241>: callq 0x400810 __dynamic_cast@plt//调用函数
0x0000000000400a0d <+246>: jmp 0x400a14 <main()+253>
0x0000000000400a0f <+248>: mov $0x0,%eax
0x0000000000400a14 <+253>: mov %rax,-0x20(%rbp) //返回值给q1
38 Derived* q2 = dynamic_cast<Derived*>(p2);
0x0000000000400a76 <+351>: mov -0x28(%rbp),%rax
0x0000000000400a7a <+355>: test %rax,%rax
0x0000000000400a7d <+358>: je 0x400a9c <main()+389>
0x0000000000400a7f <+360>: mov $0x8,%ecx //第4参数,8,偏移数据
0x0000000000400a84 <+365>: mov $0x601da0,%rdx //第3参数,类型信息
0x0000000000400a8b <+372>: mov $0x601dd8,%rsi //第2参数,类型信息
0x0000000000400a92 <+379>: mov %rax,%rdi /// 第一参数p2
0x0000000000400a95 <+382>: callq 0x400810 __dynamic_cast@plt
0x0000000000400a9a <+387>: jmp 0x400aa1 <main()+394>
0x0000000000400a9c <+389>: mov $0x0,%eax
0x0000000000400aa1 <+394>: mov %rax,-0x18(%rbp)//返回值给q2
注意: -0x18(%rbp)与-0x20(%rbp)是相差8个byte 而不是2个byte, 一走神有点犯晕.
刚好存储长整形地址.
5.4 为什么指针类型老是变来变去的,是什么类型就是什么类型,不变不好吗?
不变确实挺好,能不变就不要变.
但是c++的2大要点是继承和多态. 另一大要点是封装这就不说了.
就是例子中一个类继承了2个类,本来你用导出类指针是什么都能访问到的.
但是,如果有一个函数,它要求你传入基类的地址而不是导出类地址,这就需要转换了.
把地址调一调,才能访问到正确的数据.
用基类地址还能访问到继承类的函数(虚函数),这就是多态.
运行结果:
$ ./Test
p1:0x16b6e70,p2:0x16b6e78,pd:0x16b6e70
p1:0x16b6e70,q1:0x16b6e70
q1:0x16b6e70,q2:0x16b6e70,p1:0x16b6e70,p20x16b6e78
from Derived:compose
对运行结果的解释
$ ./Test
//p1和p2是不等的(从一个导出类对象地址转换成的两个基类地址是不等的),
//p1等于pd(其中一个基类地址跟导出类地址相等)
p1:0x16b6e70,p2:0x16b6e78,pd:0x16b6e70
//这是逆变换, 转换出的导出类与基类地址相同,但另一个导出类和基类地址不同
p1:0x16b6e70,q1:0x16b6e70
//2个基类转换出了相同的导出类地址q1,q2, 这说明它们是一个对象
q1:0x16b6e70,q2:0x16b6e70,p1:0x16b6e70,p20x16b6e78
from Derived:compose
5.4 为什么要用 dynamic_cast ?
从上边例子中我们看出, static_cast 直接计算了偏移,很简洁, 而dynamic_cast
还要去调用函数__dynamic_cast@plt 才能得到地址,显然代价更高.
那为什么还要用dynamic_cast, 全部改为static_cast 不好吗?
上边的例子是用不着dynamic_cast, 但是, 这里有但是…
我写了一个函数, 传来的是基类指针, 我不知道它真实身份是那种子类,就需要动态指针变换,由此判定它是什么类型. 这就是运行期判断,
测试代码:
#include <iostream>
using namespace std;
class Base {virtual void foo(){}
};
class Derived1 : public Base
{
};class Derived2 : public Base
{
};void deal_it(Base *b)
{auto d1 = dynamic_cast<Derived1 *>(b);
// auto d1 = static_cast<Derived1 *>(b);if(d1){// 处理Derived1类型 cout<<"is Derived1 type\n";return;}auto d2 = dynamic_cast<Derived2 *>(b);
// auto d2 = static_cast<Derived2 *>(b);if(d2){// 处理Derived2类型 cout<<"is Derived2 type\n";return;}
}
int main()
{auto b = new Derived2();deal_it(b);return 0;
}
测试结果:
$ ./Test
is Derived2 type
如果你将dynamic_cast改称static_cast, 那就得不到正确结果了, 因为static_cast 不管三七二十一,把指针加个偏移就返回了.
这样转换的指针不为0,就永远只走第一条了.
至于为什么dynamic_cast 能正确工作, 就不再这里说明了.
自定义的类型变换就不在本博举例了,可参考其它博客.
相关文章:
c/c++数据类型转换.
author: hjjdebug date: 2025年 05月 18日 星期日 20:28:52 CST descrip: c/c数据类型转换. 文章目录 1. 为什么需要类型转换?1.1 发生的时机:1.2 常见的发生转换的类型: 2. c语言的类型转换: (Type) value2.1 c语言的类型变换是如何实现的? 规则是什么? 3. c 的static_cast…...

Django 项目的 models 目录中,__init__.py 文件的作用
在 Django 项目的models/init.py文件中,这些导入语句的主要作用是将各个模型类从不同的模块中导入到models包的命名空间中。这样做有以下几个目的: 简化导入路径 当你需要在项目的其他地方使用这些模型时,可以直接从models包导入,…...
实验六:FPGA序列检测器实验
FPGA序列检测器实验(远程实验系统) 文章目录 FPGA序列检测器实验(远程实验系统)一、数字电路基础知识1. 时钟与同步2. 按键消抖原理代码讲解:分频与消抖3. 有限状态机(FSM)设计代码讲解:状态机编码与转移4. 边沿检测与信号同步5. 模块化设计二、实验数字电路整体思想三…...

网络的知识的一些概念
1.什么是局域网,什么是广域网 局域网(Local area network)也可以称为本地网,内网,局域网有这几个发展经历: 最开始电脑与电之间是直接用网线连接的 再后来有了集线器() 再后来出…...

芋道项目,商城模块数据表结构
一、需求 最近公司有新的业务需求,调研了一下,决定使用芋道(yudao-cloud)框架,于是从github(https://github.com/YunaiV/yudao-cloud)上克隆项目,选用的是jdk17版本的。根据项目启动手册&#…...
yarn任务筛选spark任务,判断内存/CPU使用超过限制任务
yarn任务筛选spark任务,判断内存/CPU使用超过限制任务 curl -s “http://it-cdh-node01:8088/ws/v1/cluster/apps?statesRUNNING” | jq ‘.apps.app | map(select(.applicationType “SPARK” ) | select(.allocatedMB > 102400 or .allocatedVCores > 50)…...

【氮化镓】HfO2钝化优化GaN 器件性能
2025年,南洋理工大学的Pradip Dalapati等人在《Applied Surface Science》期刊发表了题为《Role of ex-situ HfO2 passivation to improve device performance and suppress X-ray-induced degradation characteristics of in-situ Si3N4/AlN/GaN MIS-HEMTs》的文章。该研究基…...
c#的内存指针操作(仅用于记录)
c#也可以直接操作内存指针,如下为示例: unsafe {byte[] a {1,2,3};fixed (byte* p1 a, p2 &a[^1]){Debugger.Log(1, "test", $"max index:{p2-p1}");Debugger.Log(1, "test", $"address:{(long)p1:X}")…...
常见机器学习算法简介:回归、分类与聚类
机器学习说到底,不就三件事: 预测一个数 —— 回归 判断归属哪个类 —— 分类 自动把数据分组 —— 聚类 别背术语,别管定义,先看问题怎么解决。 一、回归(Regression) 干嘛的? 模型输出一…...
SpringBoot项目里面发起http请求的几种方法
在Spring Boot项目中发起HTTP请求的方法 在Spring Boot项目中,有几种常用的方式可以发起HTTP请求,以下是主要的几种方法: 1. 使用RestTemplate (Spring 5之前的主流方式) // 需要先注入RestTemplate Autowired private RestTemplate restT…...
Linux下Nginx源码安装步骤详解
以下是在Linux系统下从源码安装Nginx的详细步骤及解释: 1. 下载Nginx源码 步骤: wget http://nginx.org/download/nginx-1.25.3.tar.gz tar -zxvf nginx-1.25.3.tar.gz cd nginx-1.25.3解释: wget:从官网下载Nginx源码包&#…...

SQLMesh 增量模型从入门到精通:5步实现高效数据处理
本文深入解析 SQLMesh 中的增量时间范围模型,介绍其核心原理、配置方法及高级特性。通过实际案例说明如何利用该模型提升数据加载效率,降低计算资源消耗,并提供配置示例与最佳实践建议,帮助读者在实际项目中有效应用这一强大功能。…...

Zookeeper 入门(二)
4. Zookeeper 的 ACL 权限控制( Access Control List ) Zookeeper 的ACL 权限控制,可以控制节点的读写操作,保证数据的安全性,Zookeeper ACL 权 限设置分为 3 部分组成,分别是:权限模式(Scheme)、授权对象(…...

【架构篇】安全架构-双向认证
mTLS(Mutual TLS)详解:双向认证的原理、流程与实践 摘要 mTLS(Mutual TLS)是一种基于数字证书的双向身份验证协议,广泛应用于微服务通信、金融交易等高安全场景。本文深入解析mTLS的工作原理、认证流程、W…...

负载均衡—会话保持技术详解
一、会话保持的定义 会话保持(Session Persistence)是一种负载均衡策略,其核心机制是确保来自同一客户端的连续请求,在特定周期内被定向到同一台后端服务器进行处理。这种机制通过记录和识别客户端的特定标识信息,打破…...

Flask快速入门和问答项目源码
Flask基础入门 源码: gitee:我爱白米饭/Flask问答项目 - 码云 目录 1.安装环境2.【debug、host、port】3.【路由params和query】4.【模板】5.【静态文件】6.【数据库连接】6.1.安装模块6.2.创建数据库并测试连接6.3.创建数据表6.4.ORM增删改查 6.5.ORM模…...

go语法大赏
前些日子单机房稳定性下降,找了好一会才找到真正的原因。这里面涉及到不少go语法细节,正好大家一起看一下。 一、仿真代码 这是仿真之后的代码 package mainimport ("fmt""go.uber.org/atomic""time" )type StopSignal…...

软件工程各种图总结
目录 1.数据流图 2.N-S盒图 3.程序流程图 4.UML图 UML用例图 UML状态图 UML时序图 5.E-R图 首先要先了解整个软件生命周期: 通常包含以下五个阶段:需求分析-》设计-》编码 -》测试-》运行和维护。 软件工程中应用到的图全部有:系统…...
R-tree详解
R-tree 是一种高效的多维空间索引数据结构,专为快速检索空间对象(如点、线、区域)而设计。它广泛应用于地理信息系统(GIS)、计算机图形学、数据库等领域,支持范围查询、最近邻搜索等操作。以下是其核心原理…...

AAAI2024 | 基于特征多样性对抗扰动攻击 Transformer 模型
Attacking Transformers with Feature Diversity Adversarial Perturbation 摘要-Abstract引言-Introduction相关工作-Related Work方法-Methodology实验-Experiments结论-Conclusion 论文链接 本文 “Attacking Transformers with Feature Diversity Adversarial Perturbatio…...

关于数据湖和数据仓的一些概念
一、前言 随着各行业数字化发展的深化,数据资产和数据价值已越来越被深入企业重要发展的战略重心,海量数据已成为多数企业生产实际面临的重要问题,无论存储容量还是成本,可靠性都成为考验企业数据治理的考验。本文来看下海量数据存储的数据湖和数据仓,数据仓库和数据湖,…...
鸿蒙OSUniApp制作自定义的下拉菜单组件(鸿蒙系统适配版)#三方框架 #Uniapp
UniApp制作自定义的下拉菜单组件(鸿蒙系统适配版) 前言 在移动应用开发中,下拉菜单是一个常见且实用的交互组件,它能在有限的屏幕空间内展示更多的选项。虽然各种UI框架都提供了下拉菜单组件,但在一些特定场景下&…...
C++面试2——C与C++的关系
C与C++的关系及核心区别的解析 一、哲学与编程范式:代码组织的革命 过程式 vs 多范式混合 C语言是过程式编程的典范,以算法流程为中心,强调“怎么做”(How)。例如,实现链表操作需手动管理节点指针和内存。 C++则是多范式语言,支持面向对象(OOP)、泛型编程(模板)、函…...

常用的Java工具库
1. Collections 首先是 java.util 包下的 Collections 类。这个类主要用于操作集合,我个人非常喜欢使用它。以下是一些常用功能: 1.1 排序 在工作中,经常需要对集合进行排序。让我们看看如何使用 Collections 工具实现升序和降序排列&…...
基于LabVIEW的双音多频系统设计
目录 1 系统设计概述 双音多频(Dual-Tone Multi-Frequency, DTMF)信号是一种广泛应用于电话系统中的音频信号,通过不同的频率组合表示不同的按键。每个按键对应两个频率,一个低频和一个高频,共同组成独特的信号。在虚拟仪器技术快速发展的背景下,利用LabVIEW等图形化编程…...

R S的EMI接收机面板
图片摘自R & S官网。 根据您提供的第一张图(设备前面板带屏幕的图像),这是 Rohde & Schwarz ESRP7 EMI Test Receiver 的正面显示界面,我将对屏幕上显示的参数逐项进行解读: 🖥️ 屏幕参数解读 左…...

[ctfshow web入门] web122
信息收集 这一题把HOME开放了,把#和PWD给过滤了 <?php error_reporting(0); highlight_file(__FILE__); if(isset($_POST[code])){$code$_POST[code];if(!preg_match(/\x09|\x0a|[a-z]|[0-9]|FLAG|PATH|BASH|PWD|HISTIGNORE|HISTFILESIZE|HISTFILE|HISTCMD|US…...
Nginx+Lua 实战避坑:从模块加载失败到版本冲突的深度剖析
Nginx 集成 Lua (通常通过 ngx_http_lua_module 或 OpenResty) 为我们提供了在 Web 服务器层面实现动态逻辑的强大能力。然而,在享受其高性能和灵活性的同时,配置和使用过程中也常常会遇到各种令人头疼的问题。本文将结合实际案例,深入分析在 Nginx+Lua 环境中常见的技术问题…...
LangChain框架-Chain 链详解
摘要 本文基于源码分析与官方文档梳理,系统解析 LangChain 框架中的核心组件 Chain 链,旨在帮助开发者深入理解其设计原理、功能分类及实践应用场景。 作为 LangChain 的核心机制,Chain 链采用管道-过滤器(Pipe-Filter)…...

Java虚拟机 - JVM与Java体系结构
Java虚拟机 JVM与Java体系结构为什么要学习JVMJava与JVM简介Java 语言的核心特性JVM:Java 生态的基石JVM的架构模型基于栈的指令集架构(Stack-Based)基于寄存器的指令集架构(Register-Based)JVM生命周期 总结 JVM与Jav…...