大白话理解lua热更原理

声明:

这里仅凭个人理解和记忆印象随笔记录,用于辅助理解生涩难懂的更专业原理表述;

忽略术语不准确或错误对应情况。

可以指正,补充,可以选择不看,纯个人理解,拒绝苛责!!!

————————————

Lua 是一种轻量脚本语言,解释性语言,类似于js,h5很容易就可以执行

相对c#,java这种写好程序后,还要编译,解释器解析成底层c++,最后转成机器语言,执行,就很麻烦

由于语言特性,lua就跳过中间编译解析处理过程,在执行语句时,根据fix标签这种,插入到对应位置,替换执行的功能逻辑,进而实现功能热更

————————————

再细节明确的,

首先语言特性
lua是一种轻量级、解释型脚本语言,还具有动态加载和运行时修改代码的能力。这导致它有支持热更能能力。

**解释型语言**:
Lua 代码不需要预编译,
运行时通过 Lua 虚拟机(LVM)逐行解释执行。

所以,修改后的 Lua 脚本可以直接被加载和运行,无需重启程序。

(每次改过脚本后,unity都得重新编译下重新运行,lua的话,就是你运行着unity,把lua丢给它,它就直接把lua执行了)


- **动态加载**:
Lua 支持在运行时动态加载代码文件
(如通过 `require` 或 `dofile`),甚至可以替换已有的函数或模块。

- **灵活性**:Lua 的弱类型和动态特性使得代码修改后不需要重新编译整个项目,只需替换脚本文件即可。

这个灵活性可以结合反射原理来理解,动态加载,可以结合多态的执行来理解

运行时,根据程序执行不同脚本,同个方法执行不同效果功能,运行时根据执行到哪里,来确定变量的类型,不需要写lua时确定变量的类型。

所以说是灵活,弱类型,简单写几个lua脚本项目,也使用过c#的引用类型和重载重写,对比下应该能理解。

—————————————

对比unity,C#的底层执行过程

C# 是编译型语言,
编译编译嘛,先编写打包,然后到合适位置再解析执行。

代码在打包时会被编译成 IL(Intermediate Language),一种中间语言

中间语言最后在运行时,通过 JIT(Just-In-Time)编译为机器码。
机器码就理解成类似10101011这种,机器/底层c++能直接执行的数据

lua是解释性语言,就没有打包编译解析的过程,就直接丢给机器,机器就能直接读懂运行

(不专业的说,就是这个意思)

那个unity项目会用到各种程序集/功能库,比如unity.engine,system,unity.engine.ui,其实就是一个个打包好的c#的程序包/dll,
我们有时候用的资源插件package包,也是一类的,和项目工程来说,就是一个大包,一个小包

unity通过程序引用,或者反射来调用。但都是停止运行后,把这个库加进去后,运行时再引用到,再能执行。因为要编译解析过程

上面这些大概了解lua热更脚本和正常项目程序之间区别,也要注意中间提到的反射

unityc#通过反射在运行时来调用不同的库,程序集,通过反射确定正在运行的对象变量是什么类型之类的

因为表面我们写代码,定义变量是啥类型,用到那个库,而底层有个元数据的东西,有记录各个对象的数据信息,反射就是读这个元数据,跨程序集,找到这是啥玩意,然后来告诉你。

专业术语太难记了,可能会有出入,原理可以这样理解

——————————

再补充点计算机程序执行的底层过程,有点属于机组操作系统或编译原理方面的,就是机器执行程序那部分的,配合着理解

程序的底层执行过程一般包括以下几个主要阶段:
 
1、编译
 编译器将源程序,检查和处理,如检查有哪些关键词,语法对不对等,
将整块的程序,分析转换为目标机器的指令序列,
也就是一条条待执行处理的指令
 
2、加载
- 操作系统的加载器,将程序的代码和数据(也就是上面的一条条指令和使用的数据)从磁盘加载到内存中
 
3、执行
(就是是内存cpu处理一条条指令和数据的过程)

(1) 取指:CPU的控制器从内存中读取指令,将其放入指令寄存器。
(2)译码:指令译码器对指令进行分析,确定指令的操作码和操作数等信息
(也就是要做什么操作,加减乘除啊,操作的数据是那些啊)

(3)执行:运算器根据译码结果进行相应运算操作
(就是机器使用加法器,乘法器,按上一步译码结果处理数据得到结果的过程)
 
(4)访存:
若指令涉及访问内存数据,CPU会根据指令中的地址信息进行内存读写操作。
(也就是操作过程要执行啥东西,但东西还不在cpu里,就去磁盘里找一下)
 
(5)写回:将执行结果写回到寄存器或内存中。

可以把这个程序执行过程,结合上面lua和c#程序执行过程,理解一下

(那个我是用大白话解释了一下,可能不太准确,用来理解还行)

——————————

然后可以再回过头来再看两种语言执行过程,就没那么难理解了:

C# 代码 → C# 编译器 → IL(中间语言) → JIT/AOT 编译 → 机器码


Lua 代码 → Lua 虚拟机(LVM) → 逐行解释为操作码(OpCode) → 调用宿主语言(如 C)实现的功能


JIT(Just-In-Time):
运行时动态编译 IL 为机器码(如 PC/Android)。
AOT(Ahead-of-Time):
预先编译为机器码(如 iOS 的 IL2CPP)。

Lua 虚拟机(LVM):
 模拟 CPU 的行为,通过寄存器式虚拟机或 栈式虚拟机执行操作码。


Mono/.NET 运行时:
默认的 C# 执行环境,包含 JIT 编译器(Android/PC)或 AOT(iOS)。

IL2CPP:将 IL 转换为 C++ 代码,再编译为原生机器码,

解决iOS 的 JIT 限制(应该是iso不支持JIT编译器。支持ATO)

————————————

上面这些大概理解了,后面再看别人写的专业术语很多的解释,应该就没那么难理解了

### C++ 指针概念的大白话解释 #### 什么是指针? 指针是一个特殊的变量,它的值是另一个变量的地址。换句话说,指针存储的是内存位置而不是具体的数值[^1]。 例如,在C++中声明一个整数类型的指针: ```cpp int *ptr; ``` 这里的`*`表示这是一个指向整数类型数据的指针。如果有一个整数变量`num`: ```cpp int num = 10; ``` 那么可以通过取址运算符`&`获取这个变量的地址并赋给指针: ```cpp ptr = # ``` 此时`ptr`保存的就是`num`所在的内存地址。 #### 如何理解指针的作用? 想象一下图书馆里的书架编号系统。假设有一本书放在某个特定的位置上(比如第7排第3层),而你只需要记住这本书所在的具体坐标就能快速找到它。同样地,在计算机程序里,当创建了一个变量之后,操作系统会给其分配一段独一无二的记忆空间;如果我们想操作该变量的内容而不直接提及名称的话,则需要用到能够记录下这段记忆区域起始处的信息——这就是所谓的“指针”。 另外值得注意的一点是在定义多个同种类型的指针时,把星号紧挨着类型名可以帮助程序员好地理解和维护代码逻辑[^2]: ```cpp // 这样写清晰明了 int* p1, p2; // 只有p1是指向int的指针,p2不是 int *q1,*q2; // q1,q2都是指向int的指针 ``` #### 动态内存管理中的应用 除了作为普通变量地址外,指针还广泛应用于动态内存分配场景之中。例如使用`new`关键字可以在堆区申请一块新的未被使用的连续字节序列来构建对象实例,并返回这块新辟出来的首部位置供后续调用者存入实际的数据结构体成员或者数组元素等等[^3]。 ```cpp MyClass* obj = new MyClass(); delete obj; ``` 以上就是关于C++指针的一些基础介绍以及大白话理解方式。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值