Lua C API 完全指南 - C/C++ 与 Lua 交互详解
Lua C API 完全指南 - C/C++ 与 Lua 交互详解
深入解析 Lua C API,涵盖栈操作、数据传递、函数调用等核心机制,是理解 ToLua 框架底层原理的基础。
一、Lua C API 概述
1.1 核心概念
| 概念 | 说明 |
|---|---|
| lua_State | Lua 虚拟机实例,维护独立的执行环境 |
| Lua 栈 | C 与 Lua 交换数据的中介 |
| 索引方式 | 正索引(从底到顶)、负索引(从顶到底) |
| 伪索引 | 访问注册表、全局表等特殊位置 |
1.2 栈结构示意
1 | ┌─────────────────────────────────────────────────────────────────────────┐ |
二、压栈操作 (Push Operations)
2.1 压入基础类型
| API | 功能 | 栈变化 |
|---|---|---|
lua_pushnil |
压入 nil | 栈顶 +1 |
lua_pushboolean |
压入布尔值 | 栈顶 +1 |
lua_pushnumber |
压入数字 | 栈顶 +1 |
lua_pushinteger |
压入整数 | 栈顶 +1 |
2.2 压入字符串
| API | 功能 | 说明 |
|---|---|---|
lua_pushstring |
压入 C 字符串 (以 \0 结尾) | 自动复制 |
lua_pushlstring |
压入带长度的字符串 | 可包含 \0 |
lua_pushfstring |
格式化字符串压栈 | 类似 printf |
lua_pushvfstring |
vlist 版本格式化字符串 | 变参列表 |
lua_pushliteral |
压入字符串字面量 | 优化版本 |
2.3 压入特殊对象
| API | 功能 | 栈变化 |
|---|---|---|
lua_pushcfunction |
压入 C 函数 | 栈顶 +1 |
lua_pushcclosure |
压入 C 闭包 | 支持上值 |
lua_pushlightuserdata |
压入轻量用户数据 | 指针传递 |
lua_pushthread |
压入线程对象 | 协程支持 |
lua_pushvalue |
复制指定位置到栈顶 | 栈顶 +1 |
2.4 压栈代码示例
1 | // 压入各种类型到 Lua 栈 |
三、类型检查 (Type Checking)
3.1 类型判断函数
| API | 检查类型 | 返回值 |
|---|---|---|
lua_type |
获取类型枚举 | LUA_TNIL, LUA_TBOOLEAN, 等 |
lua_typename |
类型名称字符串 | “nil”, “boolean”, “number”, 等 |
lua_isboolean |
是否为布尔 | int (0/1) |
lua_iscfunction |
是否为 C 函数 | int (0/1) |
lua_isfunction |
是否为函数 (含 Lua 函数) | int (0/1) |
lua_islightuserdata |
是否为轻量用户数据 | int (0/1) |
lua_isnil |
是否为 nil | int (0/1) |
lua_isnone |
索引是否无效 | int (0/1) |
lua_isnoneornil |
无效或 nil | int (0/1) |
lua_isnumber |
是否为数字 | int (0/1) |
lua_isstring |
是否为字符串 | int (0/1) |
lua_istable |
是否为表 | int (0/1) |
lua_isthread |
是否为线程 | int (0/1) |
lua_isuserdata |
是否为用户数据 | int (0/1) |
3.2 Lua 类型常量
1 | // Lua 8 种基础类型 |
3.3 类型检查示例
1 | void check_types_example(lua_State *L) { |
四、取值操作 (To Operations)
4.1 获取栈元素
| API | 返回类型 | 功能 |
|---|---|---|
lua_toboolean |
int | 转为布尔值 |
lua_tocfunction |
lua_CFunction | 获取 C 函数指针 |
lua_tointeger |
lua_Integer | 获取整数值 |
lua_tonumber |
lua_Number | 获取浮点数值 |
lua_tolstring |
const char* | 获取字符串(可含 \0) |
lua_tostring |
const char* | 获取字符串 |
lua_topointer |
const void* | 获取指针(用于相等比较) |
lua_tothread |
lua_State* | 获取线程对象 |
lua_touserdata |
void* | 获取用户数据 |
4.2 取值注意事项
⚠️ 重要:所有
lua_to*函数签名:
1 type lua_toxxx(lua_State *L, int index);
L: 当前 Lua 状态机index: 栈上的位置(正索引、负索引、伪索引)
4.3 取值示例
1 | void get_values_example(lua_State *L) { |
五、栈操作 (Stack Manipulation)
5.1 栈管理函数
| API | 功能 | 栈变化 |
|---|---|---|
lua_gettop |
获取栈顶位置(即栈大小) | 无变化 |
lua_settop |
设置栈顶位置 | 可增可减 |
lua_remove |
删除指定位置元素 | 栈大小 -1 |
lua_insert |
将栈顶插入指定位置 | 栈大小不变 |
lua_replace |
用栈顶替换指定位置 | 栈大小 -1 |
lua_pop |
弹出 n 个元素 | 宏: lua_settop(L, -n-1) |
5.2 栈操作详解
lua_settop
1 | void lua_settop(lua_State *L, int index); |
| index 参数 | 效果 |
|---|---|
| 正数 | 设置栈顶为该位置,删除上方所有元素 |
| 大于当前栈大小 | 用 nil 填充到新位置 |
| 0 | 清空整个栈 |
| 负数 | 相当于 lua_gettop() + index + 1 |
1 | // lua_settop 示例 |
lua_remove
1 | void lua_remove(lua_State *L, int index); |
- 删除指定有效索引处的元素
- 上方元素下移填充空缺
- 不能使用伪索引
1 | // lua_remove 示例 |
lua_insert
1 | void lua_insert(lua_State *L, int index); |
- 将栈顶元素移动到指定位置
- 指定位置到栈顶的元素上移
- 不能使用伪索引
1 | // lua_insert 示例 |
lua_replace
1 | void lua_replace(lua_State *L, int index); |
- 用栈顶元素替换指定位置的值
- 弹出栈顶(栈大小 -1)
- 其他元素位置不变
1 | // lua_replace 示例 |
5.3 栈操作宏
1 | // 常用宏定义 |
六、函数调用 (Function Call)
6.1 lua_call 详解
1 | void lua_call(lua_State *L, int nargs, int nresults); |
调用协议:
1 | ┌─────────────────────────────────────────────────────────────────────────┐ |
6.2 函数调用参数
| 参数 | 说明 | 特殊值 |
|---|---|---|
nargs |
参数个数 | 必须匹配实际压入的参数数 |
nresults |
期望结果数 | LUA_MULTRET 表示接受所有返回值 |
6.3 函数调用示例
1 | // 调用 Lua 函数示例 |
七、全局表与注册表
7.1 表结构对比
| 特性 | 全局表 _G | 注册表 |
|---|---|---|
| 用途 | Lua 代码全局变量存储 | C 代码私有数据存储 |
| 访问方式 | LUA_GLOBALSINDEX |
LUA_REGISTRYINDEX |
| 可见性 | Lua 代码可见 | 仅 C 代码可见 |
| 生命周期 | 与 lua_State 相同 | 与 lua_State 相同 |
7.2 注册表使用
1 | // 注册表存储 C 对象示例 |
八、完整示例
8.1 C 调用 Lua 函数
1 |
|
8.2 Lua 调用 C 函数
1 |
|
九、API 速查表
9.1 栈操作速查
| 操作 | API | 备注 |
|---|---|---|
| 获取栈大小 | lua_gettop |
返回栈顶索引 |
| 设置栈大小 | lua_settop |
可填充 nil 或删除 |
| 弹出元素 | lua_pop |
宏: lua_settop(L, -n-1) |
| 删除元素 | lua_remove |
上方元素下移 |
| 插入元素 | lua_insert |
栈顶插入指定位置 |
| 替换元素 | lua_replace |
栈顶替换,弹出栈顶 |
9.2 压栈速查
| 类型 | API |
|---|---|
| nil | lua_pushnil |
| 布尔 | lua_pushboolean |
| 数字 | lua_pushnumber, lua_pushinteger |
| 字符串 | lua_pushstring, lua_pushlstring, lua_pushfstring |
| C 函数 | lua_pushcfunction, lua_pushcclosure |
| 表 | lua_newtable, lua_createtable |
| 用户数据 | lua_newuserdata, lua_pushlightuserdata |
9.3 检查速查
| 类型 | 检查函数 | 获取函数 |
|---|---|---|
| nil | lua_isnil |
- |
| 布尔 | lua_isboolean |
lua_toboolean |
| 数字 | lua_isnumber |
lua_tonumber, lua_tointeger |
| 字符串 | lua_isstring |
lua_tostring, lua_tolstring |
| 表 | lua_istable |
- |
| 函数 | lua_isfunction, lua_iscfunction |
lua_tocfunction |
| 用户数据 | lua_isuserdata, lua_islightuserdata |
lua_touserdata |
| 线程 | lua_isthread |
lua_tothread |
| 任意类型 | lua_type |
- |
十、总结
| 主题 | 要点 |
|---|---|
| 栈机制 | C 与 Lua 交换数据的唯一中介 |
| 索引方式 | 正索引从底到顶,负索引从顶到底 |
| 压入数据 | lua_push* 系列,栈顶 +1 |
| 获取数据 | lua_to* 系列,不改变栈 |
| 类型检查 | lua_is* 和 lua_type |
| 函数调用 | lua_call 需按协议压入函数和参数 |
| 注册表 | C 代码私有存储,与 _G 隔离 |
💡 学习建议:
- 熟记栈的索引方式(正/负索引)
- 注意每次操作后栈的状态变化
- 使用
lua_gettop()检查栈大小进行调试- 注意
lua_to*不改变栈,lua_push*增加栈- 注册表用于存储 C 代码的私有数据
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 1487842110@qq.com
本文是原创文章,采用CC BY-NC-SA 4.0协议,完整转载请注明来自新诸子
