一、栈
Lua 和 C 之间的通讯主要组件是无处不在的虚拟栈,两者间的数据交换都是通过这个栈进行。
栈中可以保存 Lua 任意类型的值。
1、Lua 和 C 之间的数据交互存在的差异
- Lua 是动态类型,C 是静态类型,两者不匹配
- Lua 是自动内存管理,C 是手动内存管理
2、垃圾收集器
Lua 与 C/C++ 的数据交互,都是通过在一方压栈(存入数据),在另一方出栈(获取数据),所以栈是能感知到数据的。
而栈是 LuaState 的一部分,所以垃圾收集器能够知道 C/C++ 正在使用哪些数据,也就知道该如何管理内存。
3、栈操作原则
Lua 的栈严格遵守 LIFO (Last in first out,后进先出) , Lua 中只有栈顶的部分会发生改变。
C 语言代码则有更大的自由度,可以检视栈中的任何一个元素,甚至可以在栈的任意位置插入或删除元素。
二、C++ 压栈
针对每一种能用 C 语言直接表示的 Lua 数据类型,C API 中都有一个对应的压栈函数
下面是 C++ 所有的压栈函数
C API 函数 | 描述 |
---|---|
void (lua_pushnil) (lua_State *L); | 将 nil 压栈 |
void (lua_pushnumber) (lua_State *L, lua_Number n); | 将 双精度浮点数 压栈 |
void (lua_pushinteger) (lua_State *L, lua_Integer n); | 将 整型数 压栈 |
void (lua_pushboolean) (lua_State *L, int b); | 将 布尔值 压栈 |
const char *(lua_pushstring) (lua_State *L, const char *s); | 将 字符串(以 "\0" 结尾) 压栈 |
const char *(lua_pushlstring) (lua_State *L, const char *s, size_t len); | 将 字符串(会结合 "\0" 和长度的参数决定字符串的长度) 压栈 |
const char *(lua_pushvfstring) (lua_State *L, const char *fmt,va_list argp); | 将 字符串(格式化字符串,接收可变参数) 压栈 |
const char *(lua_pushfstring) (lua_State *L, const char *fmt, ...); | 将 字符串(格式化字符串) 压栈 |
void (lua_pushcclosure) (lua_State *L, lua_CFunction fn, int n); | 将 C 函数 压栈 |
void (lua_pushlightuserdata) (lua_State *L, void *p); | 将 用户数据 压栈 |
int (lua_pushthread) (lua_State *L); | 将 线程状态(即 lua_State ) 压栈 |
举个例子:
lua_pushcclosure
和lua_pushlightuserdata
后续文章进行分享
使用以上的所有 api ,进行相应数据类型的压栈
cpp
lua_pushnil(L);
lua_pushnumber(L, 0.1);
lua_pushnumber(L, 0);
lua_pushinteger(L, 10);
lua_pushboolean(L, 1);
// lua_pushstring 会在 "\0" 终止
lua_pushstring(L, "lua_pushstring hello 江澎涌\0jiang peng yong");
// lua_pushlstring 同样会在 "\0" 终止,但会结合考虑长度参数
const char *sayHello = "lua_pushlstring hello jiang peng yong.\0 会被忽略的字符";
lua_pushlstring(L, sayHello, strlen(sayHello) - 6);
// lua_pushvfstring
const char *name = "jiang peng yong";
int age = 29;
showInfo(L, name, age);
lua_pushfstring(L, "lua_pushfstring name: %s, age: %d", name, age);
lua_pushthread(L);
showInfo
函数如下所示,为了能演示 va_list
和 lua_pushvfstring
的结合使用
cpp
void showInfo(lua_State *L, ...) {
va_list args;
// 用于开启遍历可变参数,第二个参数是可变参数列表的前一参数
va_start(args, L);
// 在这里可以使用 va_list 对象 args
lua_pushvfstring(L, "lua_pushvfstring name: %s, age: %d", args);
va_end(args);
}
最后通过以下的函数将栈打印出来,就一目了然了
这一函数主要使用了以下的 api ,先有一个大概了解,接下来的小节会更加详细的讲解
lua_gettop
获取栈中元素个数lua_typename
获取类型名称lua_toxxx
获取栈对应索引的元素值,并转为相应类型 xxx
cpp
void stackDump(lua_State *L) {
int i;
// 获取栈中元素个数
int top = lua_gettop(L);
printf("栈顶\n");
for (i = top; i >= 1; i--) {
// 元素类型
int t = lua_type(L, i);
// 元素类型名称
printf("^ typename: %s, ", lua_typename(L, t));
switch (t) {
case LUA_TSTRING: // 字符串类型
printf("value: '%s'", lua_tostring(L, i));
break;
case LUA_TBOOLEAN: // 布尔类型
printf(lua_toboolean(L, i) ? "value: true" : "value: false");
break;
case LUA_TNUMBER: // 数值类型
if (lua_isinteger(L, i)) { // 是否整型
printf("value(integer): %lld", lua_tointeger(L, i));
} else { // 浮点类型
printf("value(number): %g", lua_tonumber(L, i));
}
break;
default: // 其他类型
// 类型名称
printf("value: %s", lua_typename(L, t));
}
printf(" \n");
}
printf("栈底\n");
printf("\n");
}
打印之后,刚才一系列操作的栈内容如下:
cpp
栈顶
^ typename: thread, value: thread
^ typename: string, value: 'lua_pushfstring name: jiang peng yong, age: 29'
^ typename: string, value: 'lua_pushvfstring name: jiang peng yong, age: 29'
^ typename: string, value: 'lua_pushlstring hello jiang peng'
^ typename: string, value: 'lua_pushstring hello 江澎涌'
^ typename: boolean, value: true
^ typename: number, value(integer): 10
^ typename: number, value(number): 0
^ typename: number, value(number): 0.1
^ typename: nil, value: nil
栈底
1、lua_pushlstring、lua_pushstring、lua_pushvfstring、lua_pushfstring 的区别
四个函数都是将字符串压入到栈中,只是参数上有些不同
lua_pushstring
:传入的字符串则是以 \0
为终止符,第一个 "\0" 之后的内容会被忽略。
lua_pushlstring
:相比 lua_pushstring
多了一个额外的长度参数(第三个参数),同样会以 \0
为终止符,第一个 "\0" 之后的内容会被忽略。
lua_pushvfstring
:将 argp
参数按照 fmt
参数格式化字符串,argp
是一个 va_list
类型的变长参数指针。
lua_pushfstring
:将 ...
参数按照 fmt
参数格式化字符串。
可以通过上面的例子感受到用法上的不同
Lua 和 C/C++ 的字符串不同:Lua 不是以 \0 作为结尾, 他可以包含任意的二进制数据。
值得注意: Lua 语言不会保留指向 "外部字符串" 或指向 "除静态的 C 语言函数外的任何外部对象" 的指针。对于不得不保留的字符串,Lua 要么生成一个内部副本,要么复用已有的字符串。所以一旦 lua_pushlstring 此类字符串压栈的函数执行完后,即使立即释放或修改缓冲区也不会出现任何问题。
2、lua_Number 类型
lua_Number 默认为 double
从 Lua 的源码默认的宏定义可以证明
cpp
#define LUA_FLOAT_DEFAULT LUA_FLOAT_DOUBLE
#define LUA_FLOAT_TYPE LUA_FLOAT_DEFAULT
// 对 LUA_FLOAT_DEFAULT 的使用如下,其他的代码省略了
#elif LUA_FLOAT_TYPE == LUA_FLOAT_DOUBLE /* }{ double */
#define LUA_NUMBER double
可以通过改变 LUA_FLOAT_TYPE
的宏定义,达到 lua_Number
真正类型不同
LUA_FLOAT_TYPE
可以定义为
LUA_FLOAT_FLOAT
则lua_Number
被定义为 floatLUA_FLOAT_DOUBLE
(默认设定) 则lua_Number
被定义为 doubleLUA_FLOAT_LONGDOUBLE
则lua_Number
被定义为 long double
这些配置是在 Lua 源文件的 luaconf.h 文件中
3、lua_Integer 类型
和 lua_Number 一样,lua_Integer 也可以进行默认类型的配置
lua_Integer 默认为 long long(有符号 64 位整型)
cpp
#define LUA_INT_DEFAULT LUA_INT_LONGLONG
#define LUA_INT_TYPE LUA_INT_DEFAULT
#elif LUA_INT_TYPE == LUA_INT_LONGLONG /* }{ long long */
/* use presence of macro LLONG_MAX as proxy for C99 compliance */
#if defined(LLONG_MAX) /* { */
/* use ISO C99 stuff */
#define LUA_INTEGER long long // 这里
可以通过改变 LUA_INT_TYPE
的宏定义,达到 lua_Integer
真正类型不同
LUA_INT_INT
则lua_Integer
被定义为 intLUA_INT_LONG
则lua_Integer
被定义为 longLUA_INT_LONGLONG
则lua_Integer
被定义为 long long
这些配置是在 Lua 源文件的 luaconf.h 文件中
三、检查栈空间
对于大多数情况下,栈的空间是够用的,至少有 20 个空闲位置,由 LUA_MINSTACK
进行定义,但是有时如果需要较多的空间时则需要进行检查是否够用,此时 Lua 并不会进行保证安全。
cpp
// 在 lua.h 文件中可以看到
/* minimum Lua stack available to a C function */
#define LUA_MINSTACK 20
1、lua_checkstack
可以通过 lua_checkstack
进行检查是否够用
cpp
int (lua_checkstack) (lua_State *L, int n);
参数:
- L: Lua 的状态
- n: 需要的空间数量
返回值:
如果空间够的就返回 1 ,否则返回 0
举个例子:
cpp
int checkSize = lua_checkstack(L, 100000000); --> 0
checkSize = lua_checkstack(L, 10); --> 1
2、luaL_checkstack
可以使用辅助库的 luaL_checkstack
函数检查是否够用
cpp
void (luaL_checkstack) (lua_State *L, int sz, const char *msg);
可以通过具体实现得知,其实调用的也是 lua_checkstack
,只是会在不够空间时,抛出异常同时终止执行
cpp
LUALIB_API void luaL_checkstack (lua_State *L, int space, const char *msg) {
if (l_unlikely(!lua_checkstack(L, space))) {
if (msg)
luaL_error(L, "stack overflow (%s)", msg);
else
luaL_error(L, "stack overflow");
}
}
举个例子:
cpp
// 会抛出异常 PANIC: unprotected error in call to Lua API (stack overflow (Not enough space.))
luaL_checkstack(L, 100000000, "Not enough space.");
// 正常运行
luaL_checkstack(L, 10, "Not enough space.");
四、查询元素
1、栈索引
Lua 栈的索引有两种方式:
- 第一种是从栈底往上开始计数,起始下标是 1 ,往上一个元素则加 1
- 第二种是从栈顶往下开始技术,起始下标是 -1 ,往下一个元素则减 1
举个例子:
如果栈中已经有四个元素,则栈的元素下标如下图所示
1、判断栈中元素类型
C API | 描述 |
---|---|
int (lua_isnumber) (lua_State *L, int idx); | 判断是否为数值 |
int (lua_isstring) (lua_State *L, int idx); | 判断是否为字符串(数值也会被认为是字符串) |
int (lua_iscfunction) (lua_State *L, int idx); | 判断是否为 C 函数 |
int (lua_isinteger) (lua_State *L, int idx); | 判断是否为整型数 |
int (lua_isuserdata) (lua_State *L, int idx); | 判断是否为用户数据 |
int (lua_type) (lua_State *L, int idx); | 获取元素类型 |
const char *(lua_typename) (lua_State *L, int tp); | 获取类型名称 |
2、lua_type 函数
lua_type 函数返回栈中元素类型,每一种类型都有一个对应的常量:
cpp
#define LUA_TNIL 0
#define LUA_TBOOLEAN 1
#define LUA_TLIGHTUSERDATA 2
#define LUA_TNUMBER 3
#define LUA_TSTRING 4
#define LUA_TTABLE 5
#define LUA_TFUNCTION 6
#define LUA_TUSERDATA 7
#define LUA_TTHREAD 8
3、从栈中获取值
C API | 描述 |
---|---|
lua_Number (lua_tonumberx) (lua_State *L, int idx, int *isnum); | 获取指定索引的值,转为浮点数 |
lua_Integer (lua_tointegerx) (lua_State *L, int idx, int *isnum); | 获取指定索引的值,转为整数型 |
int (lua_toboolean) (lua_State *L, int idx); | 获取指定索引的值,转为布尔值 |
const char *(lua_tolstring) (lua_State *L, int idx, size_t *len); | 获取指定索引的值,转为字符串,len 指针会返回该字符串的长度 |
lua_Unsigned (lua_rawlen) (lua_State *L, int idx); | 获取指定索引的值原始长度,不进行元方法的调用。 1. 对于字符串类型(string),将返回字符串的字节数。 2. 对于表类型(table),将返回表的元素个数,即键值对的数量。 3. 对于用户数据类型(userdata),将回用户数据占用的字节数。 4. 对于其他类型的值,将返回 0。 |
lua_CFunction (lua_tocfunction) (lua_State *L, int idx); | 获取指定索引的值,转为 C 函数指针 |
void *(lua_touserdata) (lua_State *L, int idx); | 获取指定索引的值,转为用户数据 |
lua_State *(lua_tothread) (lua_State *L, int idx); | 获取指定索引的值,转为 lua_State 的指针 |
const void *(lua_topointer) (lua_State *L, int idx); | 获取指定索引的值,转为一个指针 |
3-1、错误处理
从 "栈中获取值的函数" 即使指定的元素类型不正确,也不会抛出异常。
C API | 错误表现 |
---|---|
lua_tonumberx | 索引的内容非数值,则返回 0 ,isnum 为 0 |
lua_tointegerx | 索引的内容非整数,则返回 0 ,isnum 为 0 |
lua_toboolean | 索引的内容为 nil 和 false 则转为 0 ,所有其他 Lua 值都转为 1 (值得注意的是数值 0 也会转为 true ) |
lua_tolstring | 索引的内容非字符串,则返回 NULL |
lua_rawlen | 从上一个表可以知道,如果类型不符合则返回 0 |
lua_tocfunction | 索引的内容非 C 函数,则返回 NULL |
lua_touserdata | 索引的内容非用户数据,则返回 NULL |
lua_tothread | 索引的内容非 lua_State 指针,则返回 NULL |
lua_topointer | 索引的内容非指针,则返回 NULL |
举些例子
继续延用第二节中的栈内容,下面的代码都是用到索引为 1 的栈元素。此时索引为 1 的栈元素是 nil 。
cpp
int isNum = false;
printf("lua_tonumberx(L, -1,&isNum) --> %f\n", lua_tonumberx(L, 1, &isNum)); // lua_tonumberx(L, -1,&isNum) --> 0.000000
printf("lua_tointegerx(L, -1,&isNum) --> %llu\n", lua_tointegerx(L, 1, &isNum)); // lua_tointegerx(L, -1,&isNum) --> 0
printf("lua_toboolean(L, -1) --> %d\n", lua_toboolean(L, 1)); // lua_toboolean(L, -1) --> 0
size_t length = 0;
printf("lua_tolstring(L, -1, &length) == nullptr --> %d\n", lua_tolstring(L, 1, &length) == nullptr); // lua_tolstring(L, -1, &length) == nullptr --> 1
printf("lua_rawlen(L, -1) --> %llu\n", lua_rawlen(L, 1)); // lua_rawlen(L, -1) --> 0
printf("lua_tocfunction(L, -1)) == nullptr --> %d\n", lua_tocfunction(L, 1) == nullptr); // lua_tocfunction(L, -1)) == nullptr --> 1
printf("lua_touserdata(L, -1) == nullptr --> %d\n", lua_touserdata(L, 1) == nullptr); // lua_touserdata(L, -1) == nullptr --> 1
printf("lua_tothread(L, -1) == nullptr --> %d\n", lua_tothread(L, 1) == nullptr); // lua_tothread(L, -1) == nullptr --> 1
printf("lua_topointer(L, -1) == nullptr --> %d\n", lua_topointer(L, 1) == nullptr); // lua_topointer(L, -1) == nullptr --> 1
3-2、lua_len 和 lua_rawlen
cpp
void (lua_len) (lua_State *L, int idx);
lua_len
会获取指定索引的值长度,会调用到元方法( lua_rawlen
则不会调用元方法 )。会根据给定索引处的值类型执行不同的操作:
- 如果值是一个字符串 string 或表 table ,它会返回字符串的长度或表中元素的个数。
- 如果值是一个用户数据 userdata ,它会尝试调用元方法
__len
来获取长度。
值得注意:
- 调用
lua_len
会在栈上产生一个新的整数值,表示获取到的长度或大小。 - 如果值没有定义长度或大小,或者索引无效,函数将抛出一个错误。
举两个例子:
继续延用第二节中的栈内容,索引为 1 的栈元素是 nil ,索引为 -2 的栈元素是字符串 string 。
cpp
// 如果获取错误会抛出异常,PANIC: unprotected error in call to Lua API (attempt to get length of a nil value)
lua_len(L, 1);
// 会将 -2 的内容长度压入栈
lua_len(L, -2);
// 获取栈顶数据进行打印
printf("lua_len(L, -2) --> %lld\n", lua_tointeger(L, -1)); // lua_len(L, 1) --> 46
// 将长度弹出
lua_pop(L, 1);
4、5.2 之前的数值类型转换 C API
在 5.2 之前,使用 lua_tonumber
进行获取数值无法提示错误,只会简单的返回 0 。
c++
#define lua_tonumber(L,i) lua_tonumberx(L,(i),NULL)
#define lua_tointeger(L,i) lua_tointegerx(L,(i),NULL)
5.2 之后(包括),增加了 lua_tonumberx
和 lua_tointegerx
两个函数,进行相应的浮点数和整数转换,可以通过最后一个参数 isnum
得知是否能转换成功。
举些例子:
继续延用第二节中的栈内容,索引为 -1 的栈元素是 thread ,索引为 2 的栈元素是 0.1 。
cpp
printf("lua_tonumber(L, -1) %f\n", lua_tonumber(L, -1)); // lua_tonumber(L, -1) 0.000000
printf("lua_tointeger(L, -1) %llu\n", lua_tointeger(L, -1)); // lua_tointeger(L, -1) 0
int isNumber = false;
printf("lua_tointegerx(L, 2, &isNumber) %lld\n", lua_tointegerx(L, 2, &isNumber)); // lua_tointegerx(L, 2, &isNumber) 0
printf("%s 转 integer 是否成功 %s\n", lua_tostring(L, 2), (isNumber == 0 ? "false" : "true")); // 0.1 转 integer 是否成功 false
printf("lua_tonumberx(L, 2, &isNumber) %f\n", lua_tonumberx(L, 2, &isNumber)); // lua_tonumberx(L, 2, &isNumber) 0.100000
printf("%s 转 number 是否成功 %s\n", lua_tostring(L, 2), (isNumber == 0 ? "false" : "true")); // 0.1 转 number 是否成功 true
5、lua_tolstring
cpp
const char *(lua_tolstring) (lua_State *L, int idx, size_t *len);
返回的是指向该字符串内部副本的指针,并将字符串的长度存入到 len
参数中,返回的副本是不能修改的(因为是 const )。
Lua 保证只要对应的字符串还在栈中,则这个指针就是有效的。当 Lua 调用的一个 C 函数返回时,Lua 就会清空栈。因此永远不要把指向 Lua 字符串的指针存放到获取该指针的函数之外。
lua_tolstring
返回的字符串会在末尾追加一个 \0
。因为字符串内容可能也含有 \0
,所以真正的内容长度通过 len 进行才是可靠的。
如果不需要知道长度,可以传递 NULL 给 len
, 当然也可以使用 lua_tostring
函数,他是一个 lua_tolstring
定义的宏。
c++
#define lua_tostring(L,i) lua_tolstring(L, (i), NULL)
举个例子:
下面两个写法是等同的
cpp
printf("lua_tolstring(L, -2, nullptr) %s\n", lua_tolstring(L, -2, nullptr)); // lua_tolstring(L, -2, nullptr) --> lua_pushfstring name: jiang peng yong, age: 29
printf("lua_tostring(L, -2) %s\n", lua_tostring(L, -2)); // lua_tostring(L, -2) --> lua_pushfstring name: jiang peng yong, age: 29
因为 lua_tostring
真正实现是 lua_tolstring
,所以异常的处理是一致的,不会抛出异常,只会返回 NULL
cpp
printf("lua_tostring(L, -1) 获取非字符串内容 %s\n", lua_tostring(L, -1)); // lua_tostring(L, -1) 获取非字符串内容 --> (null)
五、栈操作
C API | 描述 |
---|---|
int (lua_absindex) (lua_State *L, int idx); | 用于将栈索引转换为绝对值栈索引 1. 如果给定的索引 >= 0,则返回相同的值。 2. 如果给定的索引 < 0,则会将索引转换为从栈底计算的正索引值。 |
int (lua_gettop) (lua_State *L); | 获取栈中元素的个数 |
void (lua_settop) (lua_State *L, int idx); | 设置栈中元素的个数,如果之前的栈顶比新设置的值更高,则会将高出的元素丢弃;反之,该函数会向栈压入 nil 进行补足大小 |
void (lua_pushvalue) (lua_State *L, int idx); | 将指定索引上的元素的副本压入栈 |
void (lua_rotate) (lua_State *L, int idx, int n); | 将指定索引上的元素转动 n 个位置,正数表示向栈顶方向转动,负数表示向栈底方向转动 |
void (lua_copy) (lua_State *L, int fromidx, int toidx); | 将索引上的值复制到另一个索引上,并且原值不受影响 |
lua_insert(L,idx) | 将栈顶元素移动到指定位置,并上移指定位置之上的所有元素以开辟一个元素空间 |
lua_remove(L,idx) | 删除指定索引的元素,并将该元素之上的所有元素下移以填充空缺 |
lua_replace(L,idx) | 将栈顶元素设置到指定索引上的值,并将栈顶元素弹出 |
lua_pop(L,n) | 从栈中弹出 n 个元素 |
使用以下代码填充栈内容
cpp
lua_pushnumber(L, 10.8);
lua_pushstring(L, "jiang peng yong");
lua_pushnil(L);
lua_pushinteger(L, 29);
填充后栈内容如下,下面的例子就基于这一个栈进行演示
cpp
栈顶
^ typename: number, value(integer): 29
^ typename: nil, value: nil
^ typename: string, value: 'jiang peng yong'
^ typename: number, value(number): 10.8
栈底
1、lua_absindex
用于将栈索引转换为绝对值栈索引
- 如果给定的索引 >= 0,则返回相同的值。
- 如果给定的索引 < 0,则会将索引转换为从栈底计算的正索引值。
cpp
printf("lua_absindex(L, -1) --> %d\n", lua_absindex(L, -1)); // lua_absindex(L, -1) --> 4
printf("lua_absindex(L, 1) --> %d\n", lua_absindex(L, 1)); // lua_absindex(L, 1) --> 1
2、lua_pushvalue
将指定索引上的元素的副本压入栈
cpp
lua_pushvalue(L, -3);
操作后栈变为:
cpp
栈顶
^ typename: string, value: 'jiang peng yong'
^ typename: number, value(integer): 29
^ typename: nil, value: nil
^ typename: string, value: 'jiang peng yong'
^ typename: number, value(number): 10.8
栈底
3、lua_rotate
将指定索引上的元素转动 n 个位置,正数表示向栈顶方向转动,负数表示向栈底方向转动
n 为正数
cpp
lua_rotate(L, 3, 1);
操作后栈变为:
cpp
栈顶
^ typename: number, value(integer): 29
^ typename: nil, value: nil
^ typename: string, value: 'jiang peng yong'
^ typename: string, value: 'jiang peng yong'
^ typename: number, value(number): 10.8
栈底
n 为负数
cpp
lua_rotate(L, 3, -2);
操作后栈变为:
cpp
栈顶
^ typename: nil, value: nil
^ typename: string, value: 'jiang peng yong'
^ typename: number, value(integer): 29
^ typename: string, value: 'jiang peng yong'
^ typename: number, value(number): 10.8
栈底
一图胜千言
4、lua_copy
将索引上的值复制到另一个索引上,并且原值不受影响
cpp
lua_copy(L, 1, -1);
操作后栈变为:
cpp
栈顶
^ typename: number, value(number): 10.8
^ typename: string, value: 'jiang peng yong'
^ typename: number, value(integer): 29
^ typename: string, value: 'jiang peng yong'
^ typename: number, value(number): 10.8
栈底
5、lua_insert(L,idx)
将栈顶元素移动到指定位置,并上移指定位置之上的所有元素以开辟一个元素空间
lua_insert
是一个宏定义,真正实现是通过 lua_rotate
cpp
// lua_rotate(L, (idx), 1) 将索引为 idx 的元素向栈顶移动一个位置,从而达到栈顶元素移动到指定位置
#define lua_insert(L,idx) lua_rotate(L, (idx), 1)
cpp
lua_insert(L, 2);
操作后栈变为:
cpp
栈顶
^ typename: string, value: 'jiang peng yong'
^ typename: number, value(integer): 29
^ typename: string, value: 'jiang peng yong'
^ typename: number, value(number): 10.8
^ typename: number, value(number): 10.8
栈底
6、lua_remove(L,idx)
删除指定索引的元素,并将该元素之上的所有元素下移以填充空缺
lua_remove
也是一个宏定义,真正实现是通过 lua_rotate
和 lua_pop
cpp
// 主要通过两步进行实现
// 1、lua_rotate(L, (idx), -1) 将 idx 向栈底移动一个位置(就是移动到栈顶)
// 2、lua_pop(L, 1) 将栈顶弹出
#define lua_remove(L,idx) (lua_rotate(L, (idx), -1), lua_pop(L, 1))
cpp
lua_remove(L, -3);
操作后栈变为:
cpp
栈顶
^ typename: string, value: 'jiang peng yong'
^ typename: number, value(integer): 29
^ typename: number, value(number): 10.8
^ typename: number, value(number): 10.8
栈底
7、lua_replace(L,idx)
将栈顶元素设置到指定索引上的值,并将栈顶元素弹出
其实这是一个宏定义,真正实现的是通过 lua_copy
和 lua_pop
cpp
// 分为两步:
// 1、lua_copy(L, -1, (idx)) 将栈顶元素设置到索引为 idx 的位置
// 2、lua_pop(L, 1) 从栈中弹出 1 个元素,就是将栈顶元素弹出
#define lua_replace(L,idx) (lua_copy(L, -1, (idx)), lua_pop(L, 1))
cpp
lua_replace(L, 1);
操作后栈变为:
cpp
// 栈顶
栈顶
^ typename: number, value(integer): 29
^ typename: number, value(number): 10.8
^ typename: string, value: 'jiang peng yong'
栈底
// 栈底
8、lua_pop
从栈中弹出 n 个元素
lua_pop 也是一个宏定义,真正实现是通过 lua_settop
cpp
// lua_settop(L, -(n)-1) 通过 lua_settop 设置栈个数,利用负数达到从栈顶往下计算索引
#define lua_pop(L,n) lua_settop(L, -(n)-1)
cpp
lua_pop(L, 1);
操作后栈变为:
cpp
栈顶
^ typename: number, value(number): 10.8
^ typename: string, value: 'jiang peng yong'
栈底
9、lua_gettop
获取栈的元素个数
cpp
printf("lua_gettop(L) --> %d\n", lua_gettop(L)); --> lua_gettop(L) --> 2
10、lua_settop
设置栈的个数,如果之前栈顶比新设置的值高,则会将高出的元素丢弃;反之,该函数会向栈压入 nil 进行补足
当设置的比原先的栈个数大,则会使用 nil 在栈顶进行补充
cpp
lua_settop(L, 5);
操作后栈变为:
cpp
栈顶
^ typename: nil, value: nil
^ typename: nil, value: nil
^ typename: number, value(integer): 29
^ typename: number, value(number): 10.8
^ typename: string, value: 'jiang peng yong'
栈底
当设置的比原先的栈个数小,则会舍弃栈顶的值
cpp
lua_settop(L, 3);
操作后栈变为:
cpp
栈顶
^ typename: number, value(integer): 29
^ typename: number, value(number): 10.8
^ typename: string, value: 'jiang peng yong'
栈底
可以使用 lua_settop(L, 0);
清空栈
六、写在最后
Lua 项目地址:Github传送门 (如果对你有所帮助或喜欢的话,赏个star吧,码字不易,请多多支持)
如果觉得本篇博文对你有所启发或是解决了困惑,点个赞或关注我呀。
公众号搜索 "江澎涌",更多优质文章会第一时间分享与你。