C API
使用索引( index
)来引用栈中的元素。第一个被压入栈的元素索引为 1
,第二个被压入的元素索引为 2
,以此类推,我们还可以以栈顶为参照,使用负数索引来访问栈中的元素。此时, -1
表示栈顶元素(即最后被压入栈的元素), -2
表示在它之前被压入栈的元素,以此类推。例如,调用 lua_tostring(L, -1)
会将栈顶的值作为字符串返回。有些情况下从栈底对栈进行索引更加自然(即使用正数索引),而有些情况下则使用负数索引更好。
要检查栈中的一个元素是否为特定的类型, C API
提供了一系列名为 lua_is*
的函数,其中 *
可以是任意一种 Lua
数据类型。这些函数包括 lua_isnil
、 lua_isnumber
、 lua_isstring
和 lua_istable
等。所有这些函数都有相同的原型:
int lua_is* (lua_State *L, int index);
实际上,函数 lua_isnumber
不会检查某个值是否为特定类型,而是检查该值是否能被转换为此特定类型。函数 lua_istring
与之类似,特别之处在于,它接受数字。
还有一个函数 lua_type
,用于返回栈中元素的类型,每一种类型都由一个对应的常量表示,包括 LUA_TNIL
、 LUA_TBOOLEAN
、 LUA_TNUMBER
、 LUA_tSTRING
等。该函数一般与 switch
语句连用。当需要检查字符串和数值是否存在潜在的强制类型转换时,该函数也同样有用。
函数 lua_to*
用于从栈中获取一个值:
int lua_toboolean (lua_State *L, int index); const char * lua_tostring (lua_State *L, int index, size_t *len); lua_State *lua_tothread (lua_State *L, int index); lua_Number lua_tonumber (lua_State *L, int index); lua_Integer lua_tointeger (lua_State *L, int index);
即使指定的元素的类型不正确,调用这些函数也不会有问题。函数 lua_toboolean
适用于所有类型,它可以按照如下的规则将任意 Lua
值转换为 C
的布尔值: nil
和 false
转换为 0
,所有其他的 Lua
值转换为 1
。对于类型不正确的值,函数 lua_tolstring
和 lua_tothread
返回 NULL
。不过,数值相关的函数都无法提示数值的类型错误,因此只能简单地返回 0
。以前我们需要调用函数 lua_isnumber
来检查类型,但 Lua5.2
引入了如下的新函数:
lua_Number lua_tonumberx (lua_State *L, int idx, int *isnum); lua_Integer lua_tointegerx (lua_State *L, int idx, int *isnum);
出口参数 isnum
返回了一个布尔值,来表示 Lua
值是否被强制转换为期望的类型。
函数 lua_tolstring
返回一个指向该字符串内部副本的指针,并将字符串的长度存入到参数 len
指定的位置。我们无法修改这个内部副本( const
表明了这一点)。
Lua
语言保证,只要对应的字符串还在栈中,那么这个指针就是有效的。当 Lua
调用的一个 C
函数返回时, Lua
就会清空栈。因此,作为规则,永远不要把指向 Lua
字符串的指针存放到获取该指针的函数之外。
函数 lua_tostring
返回的所有字符串在其末尾都会有一个额外的 \0
,不过这些字符串中也可能会有 \0
,因此可以通过第三个参数 len
获取字符串的真实长度。特别的,假设栈顶的值是一个字符串,那么如下推断永远成立:
size_t len; const char *s = lua_tolstring(L, -1, &len); /* 任意lua字符串 */ assert(s[len] == '\0'); assert(strlen(s) <= len);点击复制复制失败已复制
如果不需要长度信息,可以在调用函数 lua_tolstring
时将第三个参数设置为 NULL
。不过,使用宏 lua_tostring
会更好,因为这个宏就是用 NULL
作为第三个参数来调用函数 lua_tolstring
的。
为了演示这些函数的用法,如下示例提供了一个有用的辅助函数,它输出整个栈的内容:
static void stackDump(lua_State *L) { int i; int top = lua_gettop(L); // 栈的深度 for (i = 1; i <= top; i++) { // 循环 int t = lua_type(L, i); switch (t) { case LUA_TSTRING: { // 字符串类型 printf("'%s'", lua_tostring(L, i)); break; } case LUA_TBOOLEAN: { // 布尔类型 printf(lua_toboolean(L, i) ? "true" : "false"); break; } case LUA_TNUMBER: { // 数值类型 printf("%g", lua_tonumber(L, i)); break; } default: { // 其他类型 printf("%s", lua_typename(L, t)); break; } } printf(" "); // 输出分隔符 } printf("\n"); // 换行符 }点击复制复制失败已复制
这个函数从栈底向栈顶遍历,并根据每个元素的类型打印其值。它打印字符串时会用单引号将其括起来,对数值类型的值则使用格式 "%g"
输出,对于其他 C
语言中不存在等价类型的值(表、函数等)则值打印出他们的类型(函数 lua_typename
)可以将类型编码转换为类型名称。
在 Lua5.3
中,由于整型总是可以被强制转换为浮点型,因此仍然可以用函数 lua_tonumber
和 "%g"
的格式打印所有的数值。但是,我们倾向于将整数打印为整型,以避免损失精度。此时,我们可以用新函数 lua_isinteger
来区分整型和浮点型:
case LUA_TNUMBER: { // 数值 if lua_isinteger(L, i) // 整型 printf("%lld", lua_tointeger(L, i)); else // 浮点型 printf("%g", lua_tonumber(L, i)); break; }