系列文章目录
文章目录
- 前言
- 一、概述
- 1.1 Lua堆栈
- 二、栈操作
- 2.1 基本的栈操作
- 2.2 入栈操作函数
- 2.3 出栈操作函数
- 2.4 既入栈又出栈的操作函数
- 2.5 栈检查与类型转换函数
- 2.5 获取表数据
- 三、实例演示
- 总结
前言
Lua是一种轻量级的、高性能的脚本语言,经常被用于游戏开发、嵌入式系统和其他需要灵活、可扩展的脚本功能的应用程序中。
一、概述
-
Lua是一种解释型语言,它具有简单的语法和动态类型系统,使得它易于学习和使用。它的设计目标是成为一种嵌入式脚本语言,在C/C++程序中作为库使用。这使得Lua非常适用于游戏开发,因为它可以与其他编程语言结合使用,提供灵活的脚本功能。
-
Lua的另一个重要特点是它的高性能。与其他动态语言相比,Lua的执行速度非常快,这得益于它的精简设计和高度优化的虚拟机。这使得Lua在游戏中处理大量的数据和逻辑时表现突出。
-
除了高性能外,Lua还具有一些其他的优势。首先,它具有简洁而灵活的语法,使得编写Lua代码变得非常简单和直观。其次,Lua提供了强大的元表和闭包功能,使得它可以实现面向对象编程和函数式编程的高级特性。最后,Lua还具有可扩展性,可以通过编写C/C++扩展模块来增强其功能。
1.1 Lua堆栈
要理解Lua和C++交互,首先要理解Lua堆栈。
-
Lua和C/C++语言通信的主要方法是一个无处不在的虚拟栈。栈的特点是先进后出。
-
在Lua中,Lua堆栈就是一个struct,堆栈索引的方式可是是正数也可以是负数,区别是:正数索引1永远表示栈底,负数索引-1永远表示栈顶。
规则:
①若Lua虚拟机堆栈里有N个元素,则可以用 1 ~ N 从栈底向上索引,也可以用 -1 ~ -N 从栈顶向下索引,一般后者更加常用
②堆栈的每个元素可以为任意复杂的Lua数据类型,堆栈中没有元素的空位,隐含为包含一个“空”类型数据
特性:
若有4个元素分别入栈,则:①. 正数索引,栈底是1,然后一直到栈顶是逐渐+1,最后变成4(4大于1)
②. 负数索引,栈底是-4,然后一直到栈顶是逐渐+1,最后变成-1(-1大于-4)
索引相关:
①. 正数索引,不需要知道栈的大小,我们就能知道栈底在哪,栈底的索引永远是1
②. 负数索引,不需要知道栈的大小,我们就能知道栈顶在哪,栈顶的索引永远是-1
二、栈操作
2.1 基本的栈操作
函数 | 描述 |
---|---|
lua_push* | 将一个值压入栈顶。例如,lua_pushnil、lua_pushboolean、lua_pushnumber等。 |
lua_gettop | 获取栈顶的索引,也就是栈中元素的数量。 |
lua_settop | 设置新的栈顶索引,可以-push、pop或者修改栈中的元素数量。 |
lua_remove | 移除指定的元素。 |
lua_insert | 将栈顶元素插入到指定位置。 |
lua_pop | 弹出指定数量的元素。 |
lua_settop(L, index) | 将栈顶设置为特定索引,这样栈上索引为index的位置就是当前的“虚拟”栈顶。 |
lua_to*** | 函数获取值。 |
lua_set*** | 函数设置值。 |
2.2 入栈操作函数
这些函数会将值压入 Lua 栈中,通常是执行某些操作后产生的结果或初始化时需要的值。
函数 | 描述 |
---|---|
lua_pushnil(L) | 将 nil 值压入栈中。 |
lua_pushnumber(L, n) | 将一个数字 n 压入栈中。 |
lua_pushinteger(L, n) | 将一个整数 n 压入栈中。 |
lua_pushlstring(L, s, len) | 将一个长度为 len 的字符串 s 压入栈中。 |
lua_pushstring(L, s) | 将一个字符串 s 压入栈中(等同于 lua_pushlstring,len 会自动计算)。 |
lua_pushcclosure(L, f, n) | 将 C 函数 f 与 n 个 Lua 环境变量封装成闭包,压入栈中。 |
lua_pushboolean(L, b) | 将布尔值 b(0 或 1)压入栈中。 |
lua_pushlightuserdata(L, p) | 将一个指针 p 压入栈中(通常用于存储非 Lua 数据)。 |
lua_pushthread(L) | 将当前线程(协程)压入栈中。 |
lua_newtable(L) | 创建一个新的表并将其压入栈中。 |
lua_createtable(L, narr, nrec) | 创建一个新的表并将其压入栈中,narr 和 nrec 为初始数组和哈希部分的容量。 |
lua_newuserdata(L, size) | 创建一个新的用户数据块,并将其指针压入栈中。 |
luaL_newmetatable(L, tname) | 创建一个新的元表并将其压入栈中。 |
lua_getglobal(L, name) | 获取全局变量 name 的值并将其压入栈中。 |
lua_gettable(L, idx) | 将表 idx 中与栈顶值相对应的值压入栈中。 |
lua_getfield(L, idx, k) | 获取表 idx 中字段 k 的值并将其压入栈中。 |
lua_rawget(L, idx) | 从表 idx 中获取原始值并压入栈中(不调用元方法)。 |
lua_rawgeti(L, idx, n) | 从表 idx 中按整数索引 n 获取值并压入栈中。 |
lua_rawgetp(L, idx, p) | 从表 idx 中按指针索引 p 获取值并压入栈中。 |
luaL_loadfile(L, filename) | 加载 Lua 文件并将其作为函数压入栈中。 |
luaL_loadstring(L, s) | 加载 Lua 字符串并将其作为函数压入栈中。 |
luaL_loadbuffer(L, buff, size, name) | 加载一个缓冲区并将其作为 Lua 函数压入栈中。 |
luaL_newstate() | 创建一个新的 Lua 状态,并为其分配一个新的栈。 |
2.3 出栈操作函数
这些函数会将栈中的值弹出,以进行操作或清理栈。
函数 | 描述 |
---|---|
lua_pop(L, n) | 从栈中弹出 n 个元素。 |
lua_remove(L, idx) | 删除索引为 idx 处的值,并将栈中其上方的元素向下移动。 |
lua_replace(L, idx) | 将栈顶的值替换为索引 idx 处的值,栈顶元素被弹出。 |
lua_setglobal(L, name) | 将栈顶的值设置为全局变量 name 的值,并从栈中弹出。 |
lua_settable(L, idx) | 将栈顶的值设置为 idx 处表中的一个字段,并弹出键和值。 |
lua_setfield(L, idx, k) | 将栈顶的值设置为 idx 处表中的字段 k 的值,并弹出键和值。 |
lua_rawset(L, idx) | 将栈顶的值设置为 idx 处表中的一个字段(原始设置,不调用元方法)。 |
lua_rawseti(L, idx, n) | 将栈顶的值设置为 idx 处表中的键为整数 n 的字段。 |
lua_rawsetp(L, idx, p) | 将栈顶的值设置为 idx 处表中的键为指针 p 的字段。 |
lua_setmetatable(L, idx) | 将栈顶的值设置为 idx 处值的元表,并从栈中弹出元表。 |
lua_setuservalue(L, idx) | 将栈顶的值设置为 idx 处用户数据的用户值,并从栈中弹出值。 |
luaL_unref(L, idx, ref) | 释放引用 ref 指向的值,并将其从栈中弹出。 |
2.4 既入栈又出栈的操作函数
这些函数同时进行入栈和出栈操作。
函数 | 描述 |
---|---|
lua_getmetatable(L, idx) | 获取索引 idx 处值的元表(如果存在),并将其压入栈中;如果没有元表,栈不变。 |
lua_next(L, idx) | 获取索引 idx 处表的下一个键值对,并将键和值压入栈中,弹出上一个键。 |
lua_concat(L, n) | 将栈顶的 n 个字符串拼接起来,结果压入栈中,弹出原始字符串。 |
lua_call(L, nargs, nresults) | 调用栈顶的函数并弹出 nargs 个参数,返回 nresults 个结果压入栈中。 |
lua_pcall(L, nargs, nresults, errfunc) | 与 lua_call 类似,但支持错误处理,调用失败时错误信息压入栈中。 |
luaL_callmeta(L, idx, e) | 调用元表中字段为 e 的元方法,将栈顶的值作为参数传递,并将返回值压入栈中。 |
2.5 栈检查与类型转换函数
这些函数用于检查栈中的元素类型或将栈中的元素转换为特定类型。
函数 | 描述 |
---|---|
lua_isnil(L, idx) | 检查索引 idx 处的值是否为 nil。 |
lua_isboolean(L, idx) | 检查索引 idx 处的值是否为布尔值。 |
lua_isnumber(L, idx) | 检查索引 idx 处的值是否为数字。 |
lua_isstring(L, idx) | 检查索引 idx 处的值是否为字符串。 |
lua_istable(L, idx) | 检查索引 idx 处的值是否为表。 |
lua_isfunction(L, idx) | 检查索引 idx 处的值是否为函数。 |
lua_tonumber(L, idx) | 将栈中 idx 处的值转换为数字,如果不行返回 0。 |
lua_tointeger(L, idx) | 将栈中 idx 处的值转换为整数,如果不行返回 0。 |
lua_toboolean(L, idx) | 将栈中 idx 处的值转换为布尔值。 |
lua_tostring(L, idx) | 将栈中 idx 处的值转换为字符串。 |
lua_topointer(L, idx) | 将栈中 idx 处的值转换为指针。 |
lua_type(L, idx) | 获取栈中 idx 处的值的类型。 |
2.5 获取表数据
函数 | 描述 |
---|---|
lua_getfield(L, t_idx, key) | 从 Lua 栈上的一个表中,根据字段名(key)获取对应的值,并将这个值压入 Lua 栈顶 |
注意:
Lua 可以高效地进行数据传递、类型转换和函数调用等操作。在实际编程中,开发者需要注意栈的管理,确保栈操作符合预期,以避免栈溢出或数据丢失等问题。
三、实例演示
示例1:
lua_State *L = luaL_newstate();
luaL_openlibs(L);
// 将值推入栈中
lua_pushnumber(L, 123);
lua_pushstring(L, "Hello, World");
// 设置栈顶索引为1,即栈中第二个元素
lua_settop(L, 1);
qDebug() << "栈顶索引" << lua_gettop(L);
qDebug() << "1" << lua_tostring(L,1);
qDebug() << "2" << lua_tostring(L,2);
qDebug() << "-2" << lua_tostring(L,-2);
qDebug() << "-1" << lua_tostring(L,-1);
qDebug() << "消费堆栈----------start";
lua_pop(L,1);
qDebug() << (int)lua_tointeger(L, 1);
qDebug() << lua_tostring(L, 0);
qDebug() << "消费堆栈----------end";
// 获取并打印第二个元素
if (lua_isnumber(L, 1)) {
printf("The second element is a number: %d\n", (int)lua_tointeger(L, 1));
qDebug() << (int)lua_tointeger(L, 1);
qDebug() << lua_tostring(L, 0);
}
// 替换第二个元素为新的字符串
lua_pushstring(L, "New string");
lua_replace(L, 1);
// 再次设置栈顶
lua_settop(L, 1);
// 获取并打印修改后的第二个元素
if (lua_isstring(L, 1)) {
printf("The second element is now a string: %s\n", lua_tostring(L, 1));
}
// 清理Lua状态机
lua_close(L);
运行结果:
示例2:
lua_State *L = luaL_newstate();
luaL_openlibs(L);
// Push values onto the stack
lua_pushstring(L, "Hello");
lua_pushnumber(L, 123);
// Get the stack top index
int stack_top = lua_gettop(L);
printf("Stack top index: %d\n", stack_top);
// Set the stack top to a specific index
lua_settop(L, 5); // Pushes nil values to make the stack size 5
qDebug() << "栈大小:" << lua_gettop(L);
qDebug() << lua_tostring(L,-1);
qDebug() << lua_tostring(L,-2);
qDebug() << lua_tostring(L,-3);
qDebug() << lua_tostring(L,-4);
qDebug() << lua_tostring(L,-5);
qDebug() << "---------------";
// Remove an element
lua_remove(L, -1); // Removes the second element from the top (-2 because of the nil values)
qDebug() << "栈大小:" << lua_gettop(L);
qDebug() << lua_tostring(L,-1);
qDebug() << lua_tostring(L,-2);
qDebug() << lua_tostring(L,-3);
qDebug() << lua_tostring(L,-4);
qDebug() << lua_tostring(L,-5);
qDebug() << "---------------";
// Insert an element
lua_pushstring(L, "World");
lua_insert(L, -2); // Inserts the string "World" before the top element
qDebug() << "栈大小:" << lua_gettop(L);
qDebug() << lua_tostring(L,-1);
qDebug() << lua_tostring(L,-2);
qDebug() << lua_tostring(L,-3);
qDebug() << lua_tostring(L,-4);
qDebug() << lua_tostring(L,-5);
qDebug() << "---------------";
//Replace an element
lua_pushstring(L, "New string");
lua_replace(L, -3); // Replace the string "World" before the top element
qDebug() << lua_tostring(L,-1);
qDebug() << lua_tostring(L,-2);
qDebug() << lua_tostring(L,-3);
qDebug() << lua_tostring(L,-4);
qDebug() << lua_tostring(L,-5);
qDebug() << "---------------";
// Pop elements
lua_pop(L, 2); // Pops the top two elements
qDebug() << "栈大小:" << lua_gettop(L);
qDebug() << lua_tostring(L,-1);
qDebug() << lua_tostring(L,-2);
qDebug() << lua_tostring(L,-3);
qDebug() << lua_tostring(L,-4);
qDebug() << lua_tostring(L,-5);
qDebug() << "---------------";
lua_close(L);
运行结果:
示例3:
运行结果:
总结
Lua是一种轻量级、高性能的脚本语言,适用于游戏开发、嵌入式系统和其他需要灵活、可扩展的脚本功能的应用程序。