详解关于Lua栈介绍以及实例

AI权益加码!Claude Code、Cursor等20+工具免费用! 购周边限时加赠Coding Plan Lite,畅享主流AI工具!学习进阶更高效! 阅读详情

关于Lua栈介绍以及实例是本文要介绍的内容,主要是lua中如何使用,具体内容的实现来看本文详解。c++中载入lua的代码

 
  1. #include <> //所需要的头文件
  2. extern "C"
  3. {
  4. #include "include\lua.h"
  5. #include "include\lualib.h"
  6. #include "include\lauxlib.h"
  7. #pragma comment(lib, "lua.lib")
  8. };
  9. int main(void)
  10. {
  11. char buff[256]; //栈
  12. int error; //错误代码
  13. lua_State* L = lua_open(); //lua指针
  14. luaL_openlibs(L); //载入所有lua库
  15. //在此加入所需代码...
  16. lua_close(L); //关闭lua
  17. return 0;
  18. }

在Programming in lua中载入库的方法是分别载入5个库,分别是base、table、io、string、math,但是在使用中(lua5.1.3 + vs.net2003)发现io库在载入的时候会出现错误,程序无法继续执行,但不提示错误。

在网上查询了一下,有些人遇到的是非法引用内存(见这里),他的解决方法是改成上面代码中的方式:直接载入全部库。

在这里有一段解释:“关于luaopen_io调用失败的问题,在Lua的maillist里问了一下,有人说是因为io库里有些函数的运行是依赖于Lua建立的特定的环境,所以要用lua_call来调用,要么,就直接用luaL_openlibs来引入所有标准库。看了看帮助文档,还有Lua的源代码,似乎好像就是这么回事啊!”

再查官方文档(http://www.lua.org/manual/5.1/manual.html)中有一段:

 
  1. To have access to these libraries, the C host program should call the luaL_openlibs function,
  2. which opens all standard libraries. Alternatively, it can open them individually by calling luaopen_base (for the basic library),
  3. luaopen_package (for the package library), luaopen_string (for the string library),
  4. luaopen_table (for the table library), luaopen_math (for the mathematical library),
  5. luaopen_io (for the I/O library), luaopen_os (for the Operating System library),
  6. and luaopen_debug (for the debug library).
  7. These functions are declared in lualib.h and should not be called directly: you must call them like any other Lua C function, e.g.,
  8. by using lua_call. "

最后这句的意思是:“这些函数在lualib.h中定义并且不能直接调用:你必须以其他C函数调用方式来进行调用,例如使用lua_call。”

接着是lua_call的用法:

lua_call

原型:void lua_call (lua_State *L, int nargs, int nresults);

Calls a function.

功能:调用一个方法

调用一个函数必须按照以下的规则:首先,将要调用的函数入栈;之后,将函数参数按顺序入栈,就是说第一个参数最先入栈。最后调用lua_call;nargs是入栈的参数数。当函数被调用时弹出全部的参数和函数值。当函数返回后函数结果会压入栈。结果的数量取决于nresults(lua_call的最后一个参数)。除非nresults的值是LUA_MULTRET。以这种方式所有的函数结果都被入栈。由Lua管理栈空间中的这些返回值。函数的结果按顺序入栈(第一个元素最先入栈),所以在调用完成后最后一个参数在栈顶。

 
  1. Any error inside the called function is propagated upwards (with a longjmp).

在函数调用中产生的error会被向上传递(使用longjmp方式)。

 
  1. The following example shows how the host program may do the equivalent to this Lua code:

下面的例子展示了如何使宿主程序做如下lua代码的功能:

 
  1. a = f("how", t.x, 14)
  2. it is in C:

这是在C中:

 
  1. lua_getfield(L, LUA_GLOBALSINDEX, "f"); /* function to be called */
  2. lua_pushstring(L, "how"); /* 1st argument */
  3. lua_getfield(L, LUA_GLOBALSINDEX, "t"); /* table to be indexed */
  4. lua_getfield(L, -1, "x"); /* push result of t.x (2nd arg) */
  5. lua_remove(L, -2); /* remove 't' from the stack */
  6. lua_pushinteger(L, 14); /* 3rd argument */
  7. lua_call(L, 3, 1); /* call 'f' with 3 arguments and 1 result */
  8. lua_setfield(L, LUA_GLOBALSINDEX, "a"); /* set global 'a' */

如果看不明白(其实我也不明白)那就直接按照最上面的代码中所写的方式载入lua库吧。

学习LUA也有一些时日了,个人认为对于LUA中的栈的理解很重要,嗯,写个小文章(真的很小)

如果你看了LUA的文档,那么就应该很清楚LUA与C交互数据时都是用到LUA中所谓的stack。那么当我调用lua_open函数之后栈是什么样的呢?空的(luaopen_base等会往栈上加进一些东西)。那么至于如何操作栈上的数据,我想官方文档上已经说得很清楚了,不过最初我对于栈的顺序有一些迷糊,所以就说说这个。现在假如我有如下的一段代码:

 
  1. lua_State* L = lua_open();
  2. lua_pushnumber( L, 211 );
  3. lua_pushnumber( L, 2222 );
  4. lua_newtable( L );
  5. lua_close( L );

那么当执行完lua_newtable之后栈上有三个元素,大致就是这样:

 
  1. 211
  2. 222
  3. table

现在211是第一个元素,index为1,不过LUA也可以用负数来表示,那么现在他是多少?

 
  1. index -index value
  2. 1 -3 211
  3. 2 -2 222
  4. 3 -1 table

嗯,很简单,再看看如果我们要设置一个TABLE的值怎么做?文档中说用lua_settable或是lua_rawset(这两者有什么区别应该和这里说的无关),它们参数意义、以及准备工作都一样,-1是值,-2是键值

 
  1. lua_settable( lua_state*, int )

第一个参数是要操作的脚本环境,第二个则是要操作的表在栈上的位置

一般的写法可能是这样

 
  1. // 代码A
  2. lua_getglobal( L, "myTable" ); // 获取要设置值的table
  3. lua_pushstring( L, "hp" ); // "hp"在栈上的位置为-1
  4. lua_pushnumber( L, 211 ); // "hp"在栈上的位置变为-2,而211则是-1
  5. lua_settable( L, -3 ); // 值被正确的设置到全局变量(表)的myTable中

如果我是想把hp这个值设置到全局表中呢?一般通过调用lua_setglobal宏

 
  1. lua_pushnumber( L, 211 );
  2. lua_setglobal( L, "hp" );

就这么简单,不过我们来看看lua_setglobal这个宏

 
  1. #define lua_setglobal(L,s) \
  2. (lua_pushstring(L, s), lua_insert(L, -2), lua_settable(L, LUA_GLOBALSINDEX))

这么看来实际上我们上面的代码被替换成了

 
  1. lua_pushnumber( L, 211 );
  2. lua_pushstring( L, "hp" );
  3. lua_insert( L, -2 ); // 这一步看上去或许比较诡异,实际上是把-1的值放到lua_insert的第二个参数所指的位置,然后这个位置后面的参数往上移
  4. //这里实际上最终结果就是-1和-2对调,但从逻辑上并不是对调
  5. lua_settable( L, LUA_GLOBALSINDEX ); // 这里为什么不用lua_rawset?我认为是有原因的^@^

将上面的代码与代码A结合起来看,在lua_settable时index值不同,而它做的工作是如果发现index是LUA_GLOBALSINDEX 那么就取出全局表(还有一个LUA_REGISTERINDEX,类似),否则从stack上取元素,当然,这个栈位置取出的不是一个table就会失败。所以代码A中指定的是-3是刚从全局表中取出的myTable表(这里假设他是一个table),上面的代码片段则是取出的全局表。所以lua_settable的index是什么值都可以,只要它指向的是一个table

实际上lua中与c的接口也就主要在栈的操作上,基本上你在写一个lua与C结合的程序时你最最需要做的工作就是明白你当前栈上有什么元素以及它们的位置。我一般会在纸上画出他们的位置,如果你熟了,对于几句在一起有关联的lua调用则可以很快的看出栈的变化。比如

 
  1. lua_gettable/lua_rawget
  2. lua_pushstring( L, "hp" );
  3. lua_gettable( L, LUA_GLOBALSINDEX );

只看第一句,栈顶是一个字符串,但两句放在一起,最终栈顶是一个全局表上一个名为hp的实际值

 
  1. lua_pushstring( L, "hp" );
  2. lua_pushnumber( L, 211 );
  3. lua_settable( L, LUA_GLOBALSINDEX );

无论第二句pushnumber还是pushvalue,pushstring什么的,最终这三句执行之后对于栈来说是没有任何变化的,因为lua_settable/lua_rawset会移走-1和-2

总之,对于栈的变化,在看一个函数的文档时先看它参数又需要栈上那些位置的元素并正确设置栈上的值,看清楚他会取栈上那些位置的元素作为这个lua api调用的使用并为之把正确的值放到栈上,最后注意函数完成之后会清除/移走那些位置的元素,我想应该就没什么问题了

 
  1. lua_gettable
  2. lua_getglobal(L, "mytable") <== push mytable
  3. lua_pushnumber(L, 1) <== push key 1
  4. lua_gettable(L, -2) <== pop key 1, push mytable[1]
  5. lua_settable
  6. lua_getglobal(L, "mytable") <== push mytable
  7. lua_pushnumber(L, 1) <== push key 1
  8. lua_pushstring(L, "abc") <== push value "abc"
  9. lua_settable(L, -3) <== mytable[1] = "abc", pop key & value

lua_rawget:

用法同lua_gettable,但更快(因为当key不存在时不用访问元方法__index)

lua_rawset:

用法同lua_settable,但更快(因为当key不存在时不用访问元方法__newindex)

lua_rawgeti必须为数值键

 
  1. lua_getglobal(L, "mytable") <== push mytable
  2. lua_rawgeti(L, -1, 1) <== push mytable[1],作用同下面两行调用
  3. --lua_pushnumber(L, 1) <== push key 1
  4. --lua_rawget(L,-2) <== pop key 1, push mytable[1]

lua_rawseti必须为数值键

 
  1. lua_getglobal(L, "mytable") <== push mytable
  2. lua_pushstring(L, "abc") <== push value "abc"
  3. lua_rawseti(L, -2, 1) <== mytable[1] = "abc", pop value "abc"

lua_getfield必须为字符串

 
  1. lua_getglobal(L, "mytable") <== push mytable
  2. lua_getfield(L, -1, "x") <== push mytable["x"],作用同下面两行调用
  3. --lua_pushstring(L, "x") <== push key "x"
  4. --lua_gettable(L,-2) <== pop key "x", push mytable["x"]

lua_setfield必须为字符串

 
  1. lua_getglobal(L, "mytable") <== push mytable
  2. lua_pushstring(L, "abc") <== push value "abc"
  3. lua_setfield(L, -2, "x") <== mytable["x"] = "abc", pop value "abc"

详解:详解关于Lua栈介绍以及实例的内容介绍完了,希望通过本文的学习能对你有所帮助

深入理解 Lua 虚拟机 作者:nicochen,腾讯 IEG 游戏开发工程师本文从一个简单示例入手,详细讲解 Lua 字节码文件的存储结构及各字段含义,进而引出 Lua 虚拟机指令集和运行时的核心数据结构 Lu... 阅读详情

相关推荐

Lua操作详解

1.让我们看看下面例子C++调用LUA函数的例子的使用情况 #include "stdafx.h" int main() { //新建一个lua状态 lua_State *pLua = luaL_newstate(); if( nullptr == pLua ) { return 0; } //得到初始化大小 printf( "初始化大小:%d\n", lua_gett

F_r_e_e_x的专栏 4819

lua操作

1lua 和 C++之间的交互的基本知识:      lua 和 C++ 之间的数据交互通过堆进行,中的数据通过索引值进行定位,(就像是一个容器一样,放进去的东西都要有标号)其中顶是-1底是1,也就是第 1 个入的在底;也可以这么说:正数表示相对于底的位置(位移),负数表示相对于顶的位置(位移);(2)计算和清空中元素的操作:1、函数lua_gettop()用于返回

lovemysea的专栏 2万+

lua学习】5.lua_State

1 背景2 (寄存器数组),虚拟机,全局状态机2.1 定义在lua_State结构体中2.2 global_State 全局状态机2.3 lua_newstate 主虚拟机和全局状态机的创建2.4 lua_close 关闭虚拟机2.5 lua_newthread 新建一个协程,压,返回这个新协程2.6 lua_atpanic 设置兜底异常处理函数(用于处理未捕获异常的函数),返回旧的兜底异常处理函数3 上的地址3.1 假索引3.2 根据数字索引获取上的地址4 基本的操作API4.1 lua_g.

qq_18138105的博客 4269

Lua知识整理】——Lua

一、Lua 1lua是什么 lua类似于以下的定义, 它是在创建lua_State的时候创建的:  TValue stack[max_stack_len]  // 欲知内情可以查 lstate.c 的stack_init函数 存入的数据类型包括数值, 字符串, 指针, talbe, 闭包等, 下面是一个的例子:

suhuaiqiang_janlay的专栏 7147

Lua之·Lua与C/C++交互·Lua CAPI访问操作】

Lua是一种轻量级的、高性能的脚本语言,经常被用于游戏开发、嵌入式系统和其他需要灵活、可扩展的脚本功能的应用程序中。Lua是一种解释型语言,它具有简单的语法和动态类型系统,使得它易于学习和使用。它的设计目标是成为一种嵌入式脚本语言,在C/C++程序中作为库使用。这使得Lua非常适用于游戏开发,因为它可以与其他编程语言结合使用,提供灵活的脚本功能。Lua的另一个重要特点是它的高性能。与其他动态语言相比,Lua的执行速度非常快,这得益于它的精简设计和高度优化的虚拟机。

m0_66203017的博客 1944

lua介绍

一、Lua 要理解Lua和C++交互,首先要理解Lua。 简单来说,Lua和C/C++语言通信的主要方法是一个无处不在的虚拟的特点是先进后出。 在Lua中,Lua就是一个struct,堆索引的方式可是是正数也可以是负数,区别是:正数索引1永远表示底,负数索引-1永远表示顶。如图: lua类似于以下的定义, 它是在创建lua_State的时候创建的: TValue stack[max_stack_len] // 欲知内情可以查 lstat...

lishenglong666的专栏 5388

Lua源码分析 - 结构篇 - 数据和调用(03)

Lua结构 - 存储方式 Lua结构主要是lua_State结构上。Lua结构主要由两部分组成:数据和调用 数据:主要由一个StkId结构的数组组成。所有的数据都通过lapi.c文件中的lua_push*函数向上压入不同类型的值(数字、字符串、函数等)。L->stack指向底部。每次将一个数据压入上,指针(L->top++)都会指向到下一个结构上。在结构初...

自娱自乐的代码人 8586

lua(1)

重拾lua的学习~ 大概的看了一下,小小的lua用的内容可是不少 对于各种操作符的重载简直炉火纯青哇 scope& operator,(scope s) namespace_& operator[](scope s); void operator[](scope s)   {   }

silence_nature的专栏 536

深入理解Lua与C数据通信的

Lua与C交互的是一个重要的概念。文章首先解释了为什么要引入Lua,然后对访问常用的API进行了总结,并使用这些API的注意事项,最后从Lua源代码来看的实现原理。

MaximusZhou的专栏 1万+

Lua调用原理(Lua虚拟)

转载自:https://blog.csdn.net/zhuzhuyule/article/details/41086745 一、Lua虚拟机的 1.1先简单介绍Lua虚拟机的,如图: 规则: ①若Lua虚拟机堆里有N个元素,则可以用 1 ~ N 从底向上索引,也可以用 -1 ~ -N 从顶向下索引,一般后者更加常用 ②堆的每个元素可以为任意复杂的Lua数据类型,堆中没有元素的空位,隐含为包含一个“空”类型数据 特性: 若有4个元素分别入,则: ①. 正数索引,底是1,然

寂蝶难双,窃窃思羽. 2468

lua_pop lua_settop 研究

我知道它是退的,但是退的是底还是顶。搜索一波还是没明白,自己动手测吧... 测试程序 static int lua_test(lua_State *L) { int pop_num = lua_tointeger(L,1); lua_settop(L,0); printf("1-top %d\r\n",lua_gettop(L)); lua_pushinteger(L,31); lua_pushinteger(L,32); lua_pushint

ai5945fei的博客 6627

Lua介绍以及实例

<br />c++中载入lua的代码<br />#include <> //所需要的头文件<br />extern "C"<br />{<br />#include "include/lua.h"<br />#include "include/lualib.h"<br />#include "include/lauxlib.h"<br />#pragma comment(lib, "lua.lib")<br />};<br />int main(void)<br />{<br />        char

mm_lvw的专栏 5817

Lua介绍及C++调用Lua过程分析

C++向Lua传递数组,

ahsxsk的学习之路 935

Lua及基本操作

理解Lua Lua通过一个“虚拟”与C/C++程序进行数据交互,所有的Lua C API都是通过操作这个来完成相应的数据通信。 Lua的这个“虚拟”解决了C/C++程序与Lua程序通信的两大问题: Lua使用垃圾回收,而C/C++需要手动管理内存。 Lua使用动态类型,而C/C++使用的是静态类型。 因 为这个Lua

MyDriverC 5371

Lua初学者(四)--Lua调用原理展示(lua的堆

本文较详细的描述了 宿主语言(这里拿C++实例) 调用 Lua 时过程中的 的使用情况,最后附图 动态 展示整个过程。希望对大家提供帮助。

猪猪侠的点滴 2万+

Lua

一、Lua 1. 什么是lua lua类似于以下的定义, 它是在创建lua_State的时候创建的:  TValue stack[max_stack_len]  // 欲知内情可以查 lstate.c 的stack_init函数 存入的数据类型包括数值, 字符串, 指针, talbe, 闭包等, 下面是一个的例子:

yuan2053的博客 471

Lua的实现

一、实现的基本要点 1.数据结构- 结构为先进后出类型,FILO,类似于弹夹;它的特点在于只允许在顶进行插入和删除操作,另一端为底; 一个主要有以下几个要素 入:将数据放入到中 出:将数据从中取出 顶位置:表示当前中实际元素的数量,以及入和出的索引位置 空和满的两种状态 2.Lua中的实现思路 Lua中只有一个数据结构,就是table,所以我们肯...

mozhi 2221

Lua的编程实例

以前准备写这么篇文章,一直没动手起来,现在在这贴上我编写的一些Lua简单实例源码,跟大家分享下。 转载请注明出处!   --[[ 当函数参数很多的时候,这种用表的结构作为实参,是很好的传参方式 w = Window { x=0, y=0, width=300, height=200, title = "Lua", background="blue", border = true }

拥抱变化 6229

lua理解及lua和C++的数据交换API介绍

lua理解及lua和C++的数据交换API介绍 .(转)   2012-08-14 16:56:06|  分类: Bolt&Lua|举报|字号 订阅 1. 理解lua到底是什么?     lua类似于以下的定义, 它是在创建lua_State的时候创建的:            [cpp] view plaincopyprint

zwcwu31的专栏 1342
上一篇: 详解如何把C++对象绑定到Lua轻量级
下一篇: 详解Lua环境搭建之利用EditPlus搭建Lua开发环境
xiaodaidabin
博客等级 码龄15年 1粉丝 6原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值