Lua中的栈概念

Lua中设计"栈"的目的就是解决Lua与C的通信。
无论是Lua到C,还是C到Lua,所有的数据交换都需要通过"栈"来完成,此外还可以用"栈"来保存一些中间结果。
"栈"解决了Lua和C语言之间存在的两大差异:
        [1]. lua自带垃圾回收机制,而C需要手动显式的释放内存
        [2]. lua使用动态类型,而C使用静态类型

"栈"实际是由lua管理的,"栈"中的每个元素都能保存任何类型的lua值,lua严格按照LIFO(后进先出)规则来操作栈,这些都意味着一个C值一旦入栈,就是进入了lua的世界。
不同于lua中只能操作"栈"的顶部,C-API则拥有更大的自由度,可以操作"栈"中任意位置的元素。

C-API是一组能使C代码与Lua交互的函数,这些API实现了读写Lua中的变量、调用Lua函数、运行一段Lua代码、注册C函数到Lua等功能。
几乎所有C-API实质都是对"栈"的操作,以下是对主要的C-API进行了分类。

1. 这部分API是纯粹对"栈"上元素的一些基本操作:
    -- lua_gettop()
    返回栈顶元素的正索引,也就是获取栈中元素的个数
    备注:返回0意味着栈为空

    -- lua_settop(idx)
    将栈顶设置到一个指定位置idx,即修改栈中元素的数量
    备注:idx为正时,如果比之前的栈顶高,会向栈中压入nil来补足大小,如果比之前的栈顶低,意味着多出来的元素会被丢弃;
          idx为负时,只有丢弃功能
        -- #define lua_pop(L,n)        lua_settop(L, -(n)-1)
        由lua_settop衍生出来的API宏,用于从栈中弹出n个元素
        备注:需要确保传入的n是个正数

    -- lua_pushvalue(idx)
    将栈上指定索引idx处的值作一个拷贝压入栈顶
    备注:本函数可以操作"伪索引"

    -- lua_remove(idx)
    删除指定的有效索引idx处的元素
    备注:删除后,该位置之上的所有元素会下移一个槽位;
          本函数不可以操作"伪索引",因为其并不指向真实的栈上位置

    -- lua_insert(idx)
    弹出栈顶的值,插入到指定的有效索引idx处,并依次移动这个索引之上的元素
    备注:本函数不可以操作"伪索引",因为伪索引并不真正指向堆栈上的位置

    -- lua_replace(idx)
    弹出栈顶的值,并用该值替换指定索引idx上的值
    备注:因为是在指定索引上进行覆盖操作,所以不移动任何元素;
          本函数可以操作"伪索引"

    -- lua_checkstack(size)
    检查栈中是否有size长度的空间
    备注:栈缺省的最小槽位数量是LUA_MINSTACK,一般情况下完全足够,
          但是当遇到需要占用大量栈空间的情况时,就需要调用本函数来检查栈中是否有足够的空间

2. 这部分API专门用于往"栈"中压入一个lua类型的数据,显然,这些API操作会引起"栈"上元素的变化
    -- lua_pushnil              : 往栈中压入一个常量nil
    -- lua_pushnumber           : 往栈中压入一个双精度浮点数
    -- lua_pushinteger          : 往栈中压入一个整数
    -- lua_pushboolean          : 往栈中压入一个boolean值
    
    -- lua_pushlstring(s,len)       : 往栈中压入一个长度为len的字符串s
    -- lua_pushstring(s)            : 往栈中压入一个"\0"结尾的字符串s
    备注:lua中不会去持有指向外部(比如C中)字符串的指针,所以在通过栈往lua中传递字符串时,都会在lua中生成一份拷贝。
          基于这种机制,外部函数在操作完字符串压栈后立刻释放或修改该字符串,不会有任何问题。
    
    -- lua_pushfstring(fmt,...)     : 往栈中压入一个格式化过的字符串fmt,并返回指向这个字符串的指针
    备注:类似于C中的sprintf,但也有区别:
            [1]. 无需提供这个字符串的缓冲区,因为会由lua来管理这个缓存
            [2]. 本函数接受的格式化符号极为有限(仅支持%%、%s、%d、%f、%c)
    -- lua_pushvfstring(fmt,argp)   : 基本类似于lua_pushfstring,区别仅仅在于可变形参的格式
    
    -- lua_pushcclosure(fn,n)       : 将符合lua_CFunction格式的C函数压栈以创建一个C闭包,其中包含n个upvalue
    备注:创建C闭包的步骤:
            [1].将需要关联的任意数量upvalue依次压栈
            [2].调用本函数将C函数fn改造成C闭包,并将这个C闭包压栈
    要注意的是,本函数调用过程中首先会从栈中弹出所有的upvalue来创建C闭包,然后才是将C闭包压栈
        -- #define lua_pushcfunction(L,f)  lua_pushcclosure(L, (f), 0) : 由lua_pushcclosure衍生出来的API宏,用于将一个普通的C函数压栈
        备注:这个C函数必须按照lua_CFunction格式来定义;
              这个C函数没有upvalue(也就是意味着压入栈中的是一个普通的C函数)

    -- lua_pushlightuserdata        : 往栈中压入一个C指针
    备注:在lua中light userdata是一个像数字一样的值;
          lightuserdata没有自己的元表;
          只要指针指向的地址相同,两个lightuserdata就相等
    -- lua_newuserdata(size)        : 分配一块size大小的内存,并将其地址作为userdata压入堆栈,同时返回这个地址
    备注:在lua中完整的userdata被表示为一个对象(类似table);
          完整的userdata有自己的元表;
          一个完整的userdata只和自己相等
    
    -- lua_createtable(narr,nrec)   : 创建一个空table并压栈
    备注:这个新table将被预分配narray个元素的数组空间以及nrec个元素的非数组空间
        -- #define lua_newtable(L)     lua_createtable(L, 0, 0) : 由lua_createtable衍生出来的API宏,也是用于创建一个空table并压栈,区别在于不会预分配任何元素空间

3. 这部分API专门用于访问"栈"上的元素,并且有另外一个共同点,那就是这些API操作不会引起"栈"上元素的变化
    -- lua_type(idx)            : 返回索引idx处的元素类型
    -- lua_typename(type)       : 返回lua数据类型type对应的字符串名
    -- lua_objlen(idx)          : 返回索引idx处的元素的长度
    
    -- lua_isnumber(idx)        : 如果索引idx处的元素是LUA_TNUMBER类型返回true
    备注:LUA_TNUMBER 或者是可以转换成LUA_TNUMBER的字符串都会判断为true
    -- lua_isstring(idx)        : 如果索引idx处的元素是LUA_TSTRING类型返回true
    备注:LUA_TSTRING或者是LUA_TNUMBER都会判断为true
    -- lua_isnil(idx)           : 如果索引idx处的元素是LUA_TNIL类型返回true
    -- lua_isboolean(idx)       : 如果索引idx处的元素是LUA_TBOOLEAN类型返回true
    -- lua_isnone(idx)          : 如果索引idx处的元素不存在返回true
    -- lua_istable(idx)         : 如果索引idx处的元素是LUA_TTABLE类型返回true
    -- lua_isfunction(idx)      : 如果索引idx处的元素是函数(lua函数或c函数都可)返回true
    -- lua_iscfunction(idx)     : 如果索引idx处的元素是C函数返回true

    -- lua_to*系列API用于从指定索引处获取特定类型的值
    备注:如果指定索引处的元素不具有正确的类型,则根据特定API返回0或NULL

4. 这部分API通过"栈"实现了获取lua中的变量,并且有另外一些共同点,这些API操作会引起"栈"上元素的变化;返回的值会被压入栈顶
    -- lua_gettable(idx)        : 用于获取table中指定元素的值,类似"t[k]",本函数可能会触发__index元方法
    备注:t是指定索引idx处的值,k是栈顶(-1)处的值;
          本函数会弹出栈顶的k,然后将获得的值"t[k]"压入栈顶

    -- lua_getfield(idx,k)      : 类似lua_gettable,区别在于"k"不来自栈顶而来自入参
        -- #define lua_getglobal(L,s)  lua_getfield(L, LUA_GLOBALSINDEX, (s))   : 由lua_getfield衍生出来的API宏,用于获取全局table中指定元素s的值
        
    -- lua_rawget(idx)          : 类似lua_gettable,区别在于本函数不会触发__index元方法
    
    -- lua_rawgeti(idx,n)       : 用于获取数组中指定元素的值
    备注:跟lua_getfield的相似点在于table/array的索引都来自入参;
          跟lua_rawget的相似点在于都不会触发__index元方法

    -- lua_getmetatable(objindex)   : 获取索引objindex处的值的元表

5. 这部分API通过"栈"实现了设置lua中的变量,并且有另外一个共同点,这些API操作会引起"栈"上元素的变化
    -- lua_settable(idx)        : 用于为table中指定元素赋值,类似"t[k] = v",本函数可能会触发__newindex元方法
    备注:t是指定索引idx处的值,v是栈顶(-1)处的值,k是栈顶之下(-2)那个值
          本函数执行过程中会弹出栈中的v和k值

    -- lua_setfield(idx,k)      : 类似lua_settable,区别在于"k"不来自栈顶而来自入参
        -- #define lua_setglobal(L,s)  lua_setfield(L, LUA_GLOBALSINDEX, (s))   : 由lua_setfield衍生出来的API宏,用于为全局table中指定元素s赋值

    -- lua_rawset(idx)          : 类似lua_settable,区别在于本函数不会触发__newindex元方法

    -- lua_rawseti(idx,n)       : 用于为数组中指定元素赋值
    备注:跟lua_setfield的相似点在于table/array的索引都来自入参;
          跟lua_rawset的相似点在于都不会触发__newindex元方法

    -- lua_setmetatable(objindex)   :将栈顶的table弹出,然后将其设置为指定索引objindex的值的元表


PS: 以上API笔记基于"Lua 5.1.5"版本,从"Lua 5.2"开始,CAPI有增删变化,后续追加

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值