目录

一、Lua栈操作 - 引用类型在栈上处理方式

二、Lua栈操作 - 入栈操作

三、Lua栈操作 - 栈类型操作

四、Lua栈操作 - 从栈上获取数据

五、Lua栈操作 - 其它栈操作


上一章节讲解了Lua的栈结构,理解了上一篇的栈接口在看本片应该比较好理解。

Lua常用的栈操作API主要在lapi.c(lapi.c也提供给外部使用)文件中。

一、Lua栈操作 - 引用类型在栈上处理方式


  • Lua针对需要垃圾回收的元素,在压入栈时,都会在Lua(也就是Lua虚拟机中)生成一个副本。
  • C里面的值,被压入栈之后,lua不会在依赖这个值,而是通过拷贝副本的方式,自己管理对应的这个值了。

以lua_pushstring为例,我们填入一个字符串值的时候,lua栈函数,会将字符串生成一个内部副本。Lua不会持有指向外部字符串的指针,也不会持有指向任何其他外部对象的指针(除了C函数,因为C函数总是静态的)

/**
 * 压入一个字符串到栈L->top上
 * 会拷贝一个副本
 */
LUA_API const char *lua_pushstring (lua_State *L, const char *s) {
  lua_lock(L);
  if (s == NULL)
    setnilvalue(L->top);
  else {
    TString *ts;
    ts = luaS_new(L, s); //拷贝一个副本
    setsvalue2s(L, L->top, ts);
    s = getstr(ts);  /* internal copy's address */
  }
  api_incr_top(L);
  luaC_checkGC(L);
  lua_unlock(L);
  return s;
}

二、Lua栈操作 - 入栈操作


/**
 * 压入一个nil类型的栈到L->top上
 */
LUA_API void lua_pushnil (lua_State *L)

/**
 * 压入一个浮点数字到栈L->top上
 */
LUA_API void lua_pushnumber (lua_State *L, lua_Number n)

/**
 * 压入一个int类型数字到栈L->top上
 */
LUA_API void lua_pushinteger (lua_State *L, lua_Integer n)

/**
 * 压入一个字符串类型到栈L->top上
 */
LUA_API const char *lua_pushlstring (lua_State *L, const char *s, size_t len)

/**
 * 压入一个字符串到栈L->top上
 */
LUA_API const char *lua_pushstring (lua_State *L, const char *s)

/**
 * 压入字符串到栈L->top上
 */
LUA_API const char *lua_pushvfstring (lua_State *L, const char *fmt,
                                      va_list argp)

/**
 * 压入字符串到栈L->top上
 */
LUA_API const char *lua_pushfstring (lua_State *L, const char *fmt, ...)

/**
 * 在L->top栈上设置一个function
 * c语言闭包函数
 */
LUA_API void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n)

/**
 * 压入布尔值到L->top栈上
 */
LUA_API void lua_pushboolean (lua_State *L, int b)

/**
 * 压入用户数据地址到L->top栈上
 */
LUA_API void lua_pushlightuserdata (lua_State *L, void *p)

/**
 * 创建一个lua新线程,并将其压入栈。lua线程不是OS线程
 * LUA的线程更多理解上是协程
 */
LUA_API int lua_pushthread (lua_State *L)

三、Lua栈操作 - 栈类型操作


/**
 * 栈类型。TValue栈上是方法、数字、nil等类型
 */
LUA_API int lua_type (lua_State *L, int idx)

/**
 * 类型编号转成类型名称
 * 类型数组: luaT_typenames_[LUA_TOTALTAGS]
 * 类型:nil=null boolean=布尔 function=方法 string=字符串
 */
LUA_API const char *lua_typename (lua_State *L, int t)

/**
 * 判断是否为function栈
 */
LUA_API int lua_iscfunction (lua_State *L, int idx)

/**
 * 判断是否为int类型栈
 */
LUA_API int lua_isinteger (lua_State *L, int idx)

/**
 * 判断是否为int类型
 */
LUA_API int lua_isnumber (lua_State *L, int idx)

/**
 * 判断是否为数字类型
 */
LUA_API int lua_isstring (lua_State *L, int idx)

/**
 * 判断是否为数字类型
 */
LUA_API int lua_isuserdata (lua_State *L, int idx)

/**
 * 判断两个栈是否一样,如果一样返回1,否则返回0
 */
LUA_API int lua_rawequal (lua_State *L, int index1, int index2)

四、Lua栈操作 - 从栈上获取数据


该lua_to*批次函数,主要通过index2addr函数,寻找到操作栈CallInfo上的栈指针地址,然后获取数据。

  • idx > 0,则从操作栈底部开始寻找值
  • idx < 0,则从操作栈栈顶开始寻找值

例如:

/* lua_toboolean idx=-1 从操作栈栈顶获取最后的值 */ 
lua_pushcfunction(L, &pmain);  /* 将pmain放入L结构上 L->top值&pmain*/
lua_pushinteger(L, argc);  /* 将argc 放入L结构上  L->top值argc*/
lua_pushlightuserdata(L, argv); /* 将argv 放入L结构上 L->top值argv*/
status = lua_pcall(L, 2, 1, 0);  /* 函数操作,执行pmain 函数 do the call */
result = lua_toboolean(L, -1);  /* 获取pmain函数lua_pushboolean(L, 1) 的信号值get result */

/* idx=1 则获取操作栈从底部开始的第2个栈内容,第一个栈内容未ci->func 函数pmain的栈 */
static int pmain (lua_State *L) {
int argc = (int)lua_tointeger(L, 1); //从L->top结构上,获取argc参数  对应函数:lua_pushinteger
}

/**
 * 通过指定索引值idx,寻找调用栈上L->top的栈分片TValue值
 * 栈顶=idx=-1
 * 栈底=idx=1
 */
static TValue *index2addr (lua_State *L, int idx) {
  CallInfo *ci = L->ci;

  /**
   * idx>0 则通过栈底到栈尾寻地址方法
   */
  if (idx > 0) {
    TValue *o = ci->func + idx;
    api_check(L, idx <= ci->top - (ci->func + 1), "unacceptable index");
    if (o >= L->top) return NONVALIDVALUE;
    else return o;
  }
  else if (!ispseudo(idx)) {  /* negative index */
    api_check(L, idx != 0 && -idx <= L->top - (ci->func + 1), "invalid index");
    return L->top + idx;
  }
  else if (idx == LUA_REGISTRYINDEX)
    return &G(L)->l_registry;
  /**
   * idx<0 则通过顶到栈底寻地址方法
   */
  else {  /* upvalues */
    idx = LUA_REGISTRYINDEX - idx;
    api_check(L, idx <= MAXUPVAL + 1, "upvalue index too large");
    if (ttislcf(ci->func))  /* light C function? */
      return NONVALIDVALUE;  /* it has no upvalues */
    else {
      CClosure *func = clCvalue(ci->func);
      return (idx <= func->nupvalues) ? &func->upvalue[idx-1] : NONVALIDVALUE;
    }
  }
}

 

/**
 * 给定索引处的 Lua 值转换为 lua_Number 这样一个 C 类型
 */
LUA_API lua_Number lua_tonumberx (lua_State *L, int idx, int *pisnum)

/**
 * 把给定索引处的 Lua 值转换为 lua_Integer 这样一个有符号整数类型
 * 必须:数字/字符串类型数字
 */
LUA_API lua_Integer lua_tointegerx (lua_State *L, int idx, int *pisnum) 

/**
 * 把指定的索引处的的 Lua 值转换为一个 C 中的 boolean 值( 0 或是 1 )。 和 Lua 中做的所有测试一样,
 * lua_toboolean 会把任何 不同于 false 和 nil 的值当作 1 返回; 否则就返回 0 。 如果用一个无效索引去调用也会返回 0 。
 */
LUA_API int lua_toboolean (lua_State *L, int idx)

/**
 * 给定索引处的 Lua 值转换为一个 C 字符串
 *  如果 len 不为 NULL ,它还把字符串长度设到 *len 中。 这个 Lua 值必须是一个字符串或是一个数字; 否则返回返回 NULL 。
 *  如果值是一个数字,lua_tolstring 还会把堆栈中的那个值的实际类型转换为一个字符串。
 */
LUA_API const char *lua_tolstring (lua_State *L, int idx, size_t *len)

/**
 * 给定索引处的 Lua 值转换为一个 C 函数
 */
LUA_API lua_CFunction lua_tocfunction (lua_State *L, int idx)

/**
 * 给定索引处的值是一个完整的 userdata
 */
LUA_API void *lua_touserdata (lua_State *L, int idx)

/**
 * 把给定索引处的值转换为一个 Lua 线程(由 lua_State* 代表)。 这个值必须是一个线程;否则函数返回 NULL 。
 */
LUA_API lua_State *lua_tothread (lua_State *L, int idx)

/**
 * 把给定索引处的值转换为一般的 C 指针 (void*) 。
 * 这个值可以是一个 userdata ,table ,thread 或是一个 function
 */
LUA_API const void *lua_topointer (lua_State *L, int idx)

五、Lua栈操作 - 其它栈操作


/**
 * 通过指定索引值idx,寻找栈上L->top的栈分片TValue值
 * 栈顶=idx=-1
 * 栈底=idx=1
 */
static TValue *index2addr (lua_State *L, int idx)

/**
 * 针对lua_State进行扩容
 */
static void growstack (lua_State *L, void *ud)

/**
 * 检查lua_State的大小,如果栈小了,则扩容(默认栈大小:栈的默认尺寸是35)
 * 说明:只会不断扩容,不会缩小
 * 32/64位机器栈最大:1000000
 * 16位机器栈最大:15000
 */
LUA_API int lua_checkstack (lua_State *L, int n)

/**
 * 从*from虚拟机结构上向*to虚拟机结构上拷贝n个栈分片内容
 */
LUA_API void lua_xmove (lua_State *from, lua_State *to, int n)

/**
 * 返回LUA 栈的个数
 * 同时也是栈顶元素的索引,因为栈底是1
 */
LUA_API int lua_gettop (lua_State *L)

/**
 * 设置栈的高度,如果之前的栈顶比新设置的更高,那么高出来的元素会被丢弃,反之压入nil来补足大小
 */
LUA_API void lua_settop (lua_State *L, int idx)

 

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐