概述
- 通过标准库和
C API
,Lua
可以轻松地与C
和其他编程语言集成
语法简述
数据结构
nil
: 用于表示不存在的值。boolean
: 包括true
和false
。number
: 默认情况下,Lua 5.3
之后的版本使用双精度浮点数表示。string
: 用于表示不可变的字符序列。table
: 表是Lua
的主要数据结构,它们是关联数组,可以用来表示数组、集合、记录等。function
: 在Lua
中,函数是第一类值,可以存储在变量中,作为参数传递等。thread
: 用于表示协程。userdata
: 用于表示C
中的任意数据
函数和闭包
Lua
支持高阶函数和词法闭包。- 这允许强大和灵活的编程范式,例如函数式编程
协程
Lua
支持协程,这是一种轻量级线程的概念,可以用于编写复杂的非阻塞逻辑
元表和元方法
Lua
的表可以附加元表,定义一些特殊行为,如运算符重载
垃圾收集
Lua
使用自动内存管理,包括一个增量垃圾收集器来回收不再使用的内存
C API
1 2 |
1. <code>Lua </code>提供了一组 <code>C API</code>,允许 <code>C</code> 代码调用 <code>Lua</code> 代码,反之亦然。 1. 这使得 <code>Lua</code> 可以用于扩展和定制许多不同类型的应用程序 |
跨平台
Lua
是跨平台的,可以在许多操作系统和架构上运行
理解相关
解释器单独运行
- 下面的进程地址空间是属于
lua
解释器自己的进程地址空间 - 解释器单独运行时,和普通的程序一样,同样的进程地址空间(以下由高地址向低地址):
- 内存映射区
- 存放动态链接库和共享库,以及用于进程间通信的共享内存等
- 栈区
C
调用栈,用于存储Lua
解释器本身的函数调用信息以及与C
函数交互时的信息
- 堆区
Lua
的对象(例如表、字符串等)Lua
的虚拟栈都是在堆上分配的
- 数据区
bss
段存放未初始化的数据(全局变量或静态变量)(地址大一些)- 初始化过的数据段存放初始化的全局变量或静态变量
- 全局和静态的
Lua
变量可能位于宿主进程的数据段中,也包括了解释器自身的全局和静态数据
- 代码区
- 存放程序的可执行代码。
- 这部分内存通常是只读的,以防止程序在运行时修改自己的指令
Lua
解释器的代码本身通常存储在宿主进程的文本段中
- 内存映射区
解释器在宿主程序中运行
- 下面的进程地址空间是属于宿主自己的进程地址空间
- 宿主进程地址空间(以下由高地址向低地址):
- 内存映射区
- 存放动态链接库和共享库,以及用于进程间通信的共享内存等
- 如果
Lua
作为动态链接库嵌入,则它可能会位于内存映射段中
- 栈区
- 宿主程序和嵌入的
Lua
解释器共享相同的调用栈 - 当从
C
代码调用Lua
函数或从Lua
调用C
函数时,将使用此共享栈
- 宿主程序和嵌入的
- 堆区
Lua
的对象(例如表、字符串等)Lua
的虚拟栈都是在堆上分配的- 动态分配的其他数据结构,如协程对象,哈希表等
- 数据区
bss
段存放未初始化的数据(全局变量或静态变量)(地址大一些)- 初始化过的数据段存放初始化的全局变量或静态变量
- 全局和静态的
Lua
变量可能位于宿主进程的数据段中,也包括了解释器自身的全局和静态数据
- 代码区
- 存放程序的可执行代码。
- 这部分内存通常是只读的,以防止程序在运行时修改自己的指令。
Lua
解释器的代码本身通常存储在宿主进程的文本段中
- 内存映射区
虚拟栈
Lua
的虚拟栈为每个活动函数(包括Lua
函数和C
函数)维护一个栈帧。- 每个栈帧包含该函数的局部变量、临时值、返回地址等。
- 在运行时,
Lua
解释器将管理虚拟栈的大小和组织- 包括在调用新函数时创建新的栈帧
- 在函数返回时销毁栈帧
- 以及必要时扩展或收缩虚拟栈的大小
- 由于虚拟栈在堆区分配,所以它可以灵活地进行这些操作,而不受固定大小限制
- 虚拟栈使得
Lua
可以方便地实现诸如尾递归优化和协程等特性
- 虚拟栈使得
lua局部变量
- 局部变量存储在虚拟栈的栈帧中,而虚拟栈自身通常位于堆区。
lua全局变量
- 在
Lua
中,全局变量实际上是存储在一个特殊的表中的- 这个表被称为全局环境表(通常标识为
_G
)
- 这个表被称为全局环境表(通常标识为
- 当你在 Lua 中创建一个全局变量时,实际上是在全局环境表中创建了一个键值对
1 |
myGlobalVar = 42 |
- 全局环境表的键和值可以是除
nil
外的任何类型
表的底层实现
-
Lua
表是一种非常灵活和强大的数据结构,可以视为关联数组或哈希表 -
Lua 表的底层实现是通过两部分组合来的:
-
数组部分:
- 键是连续的正整数,不需要额外的信息来表示类型
- 这部分存储连续的整数索引的值
- 数组部分通常实现为一个连续的内存区域,每个元素包括键和值
- 通过键作为索引,可以在常数时间内访问对应的值。
-
哈希部分:
- 用于非连续整数索引和非整数键
- 键可以是任何非
nil
类型 - 在查找哈希表中的元素时,键的类型会直接用于计算哈希值和解决冲突
- 这种情况下,键的类型信息是通过哈希函数和冲突解决策略的实际实现来处理的,而不是存储为键的明确元数据
- 键可以是任何非
- 它采用标准的哈希表机制,包括冲突解决技术,以处理可能的键冲突
- 哈希部分是一个哈希表,其中每个表项包括一个键和对应的值
- 当你尝试通过一个键访问值时,
Lua
首先会计算该键的哈希值,然后使用哈希值在哈希表中查找键和对应的值 - 如果出现哈希冲突(不同的键具有相同的哈希值),
Lua
会使用开放寻址或其他冲突解决技术找到正确的表项
12_G[42] = "Hello, World!"print(_G[42]) -- 输出 "Hello, World!"关于键值
- 用于非连续整数索引和非整数键
-
根据上面的解释,键可以是数字,也可以是其他的数据类型数据
- 具体看上一条
-
关于值:
- 值是一个元素,每个元素可能包括以下内容
- 值的实际数据
- 类型信息
- 其他可能的元数据
关于协程
-
Lua
的协程是一种非抢占式的多任务处理机制,允许多个独立的控制流共享同一个线程 -
协程提供了协作式多任务的功能,其中每个协程只在显式让出(
yield
)时挂起执行,并允许另一个协程运行 -
以下是
Lua
协程的一些主要组成部分和实现细节: -
协程创建
- 可以使用
coroutine.create
来创建新的协程 - 然后使用
coroutine.resume
和coroutine.yield
来控制协程的执行 - 每个协程在内部都与一个
Lua
函数相关联,当你恢复协程时,这个函数开始执行
- 可以使用
-
协程状态
- 每个协程都有自己的状态,可以是
"running"
、"suspended"
、"normal"
或"dead"
- 状态决定了协程在何时可以恢复或挂起
- 每个协程都有自己的状态,可以是
-
协程虚拟栈
- 每个协程都有自己的虚拟栈,用于存储局部变量和其他执行状态
- 当协程挂起时,它的虚拟栈的内容会被保存,当协程恢复时,虚拟栈的内容会被恢复
- 这允许每个协程保持自己的执行上下文。
-
挂起与恢复
- 通过调用
coroutine.yield
,协程可以让出控制,将执行权交还给调用它的协程或主线程 coroutine.resume
可用于恢复先前挂起的协程的执行- 这些函数允许协程之间传递值,实现协程之间的通信。
- 通过调用
-
非抢占式
Lua
的协程调度是非抢占式的,这意味着协程必须显式让出控制权才能允许其他协程运行
-
与线程区别
- Lua 协程提供了类似于线程的并发执行功能,但它们与操作系统线程有明显的区别
- 协程共享同一个线程和内存空间,并通过协作调度而非抢占调度运行
- 这使得协程在许多情况下更轻量和更容易管理,但也意味着它们不利用多核处理器的并行能力
协程栈数据结构
- 当协程挂起时,它的虚拟栈的内容被保存在内存中与那个协程关联的数据结构中
- 这个结构保持了协程的所有执行上下文,包括:
- 调用栈:保存了协程中所有函数调用的历史,以及每个函数调用的局部变量、上值(
upvalues
)和其他相关状态 - 程序计数器:表示了协程在挂起时所在的代码位置
- 其他相关状态:包括闭包、元表、错误处理函数等
- 调用栈:保存了协程中所有函数调用的历史,以及每个函数调用的局部变量、上值(
垃圾回收机制
Lua
使用一个称为垃圾收集(Garbage Collection,GC
)的机制来自动管理内存,确保不再使用的对象被正确地回收以释放内存。Lua
的垃圾收集器是一个增量收集器,并提供一些调优参数以适应不同的使用场景- 可达性分析
Lua
垃圾收集的核心思想是通过可达性分析来确定哪些对象是“活动的”或“死亡的”。- 如果一个对象可以从根通过一系列引用访问到,那么它就被认为是活动的
- 否则,它就是死亡的
- 标记清楚算法
Lua
使用了一种称为标记-清除(Mark-and-Sweep
)的算法。该算法分为两个阶段:- 标记阶段:在这个阶段,垃圾收集器从一组根对象(例如全局变量)开始,遍历所有可从根访问的对象,并将这些对象标记为可达
- 清除机制:在这个阶段,垃圾收集器遍历所有对象,并释放那些未被标记为可达的对象的内存
- 增量收集
Lua
垃圾收集器的一个重要特性是它的增量性- 这意味着它不是一次执行完整的标记-清除周期,而是将其分为多个较小的步骤
- 这有助于减少垃圾收集过程对程序性能的影响
- 弱引用表
Lua
还支持弱引用表,这些表允许引用对象但不阻止它们被垃圾收集- 这对于实现缓存和其他需要精细控制内存管理的高级模式非常有用
- 手动控制和调优
Lua
提供了一组函数,例如collectgarbage
,允许你与垃圾收集器进行交互,执行诸如手动启动垃圾收集周期或设置收集器参数等操作
C语言中创建的表
- 不会存到全局变量环境表里,而是存在于
lua
栈里面 - 除非显示指定:
1 2 |
lua_newtable(L); // 创建一个新的表并将其推送到栈上 lua_setglobal(L, "myTable"); // 将栈顶的表赋给全局变量 "myTable" |
本文为原创文章,版权归Aet所有,欢迎分享本文,转载请保留出处!
你可能也喜欢
- ♥ Lua程序设计:二11/07
- ♥ C++_调用 Lua内容:表—传递&&获取10/12
- ♥ lua学习记述二06/13
- ♥ Lua程序设计:一10/18
- ♥ C++_调用 Lua内容:全局变量 获取&&设置10/09
- ♥ C++_调用 Lua函数10/09