nil是一个类型也只有一个nil值
除了false和nil,其他数值参与运算,都视为ture,包括0
and:第一个值为false,返回false,否则返回第二个操作数
or:第一个值不为false,返回第一个操作数,否则返回第二个操作数
x = x or y:x无值时将x赋值为y
not:永远返回真假值而不返回操作数
数值类型
分为整形和浮点,
type(3) 和 type(3.0)的类型都是number,他们之间可以相互转化,而且3 == 3.0为ture
区分类型可以用math.type()
要么区分每一个数值的类型,要么就无视类型
整形 / 整形也不会截断,自然而然转换成浮点,如果想要截断,用 整形 // 整形,3//2
取整函数:math.floor(),math.ceil(),math.modf(),分别是向负无穷、正无穷、0取整
转型:
整数 + 0.0 转为浮点型
浮点 | 0 转为整形,如果转整形的时候小数不为0,那么就产生了截断,会抛出异常,也就是说3.111不能这样转
转整形或者失败保持原来的值:return math.tointeger(x) or x
字符串
字符串参与算术运算默认视为浮点
字符串声明:单引号双引号都可
字符串长度:a = “test”; print(#a)
字符串连接:
”test1“ . . ”test“
输出:test1test2
如果是10 .. 20
,需要在..
前面加空格,不然视为浮点
多行字符串:
str =
[[
字符串内容
]]
如果字符串中间有table[table[2]]的情况,可以加任意多个=来区分头尾,等号数量相等才会匹配
str =
[===[
字符串内容;
table[table[2]];
]===]
字符串相关函数:
tonumber(字符串,2-36进制)
tostring(数值)
string.len(str)
和#取长度等价
string.rep(str,count)
把字符串重复写几次
string.reverse(str)
反转
string.lower(str)
string.upper(str)
string.sub(str, strat, end)
取子串,含首尾,索引从1开始,-1代表反向第一个
inta, intb = string.find(str, substr)
返回start和end
string.format(“%d %x %f %s”, parm4,parm4,parm4,parm4,)
分别是十进制、十六进制、浮点、字符串
表类型
表的创建、初始化:
a = {}
数组形式的表:{"11", 12, "13",}
等价于 { [1] = "11", [2] = 12, [3] = "13",}
哈希形式的表:{x = 0, y = 0,}
等价于 { ["x"] = 0, ["y"] = 0,}
两种可以混起来使用
表是一个对象,初始化时赋值给某个变量,引用+1,引用为0时对象释放
变量赋值nil引用-1
当给某个键赋值nil,表示删除
下文使用的对象:test_table = {["a"] = "11", ["b"] = 12, ["c"] = "13"}
test_table.a
等价于test_table["a"]
,就像math.sin那样,是math.[“sin”],只不过这个键里是一个函数
如果一个浮点数作为索引,并且这个浮点数可以无损转为整数,那他就是整数索引,否则还是保持浮点数索引
#test_table
井号也是可以取到表长度的,但是nil的不计入内
关于 嵌套过深的表 的 安全访问:
E = {};
val = (((test.table or E).one_floor or E).two_floor or E).three_floor
表相关的库函数:
table.insert(表对象, 插入个什么值到尾部)
table.insert(表对象, 索引位置, 插入个什么值)
table.remove(表对象删最后一个元素)
table.remove(表对象, 删哪个位置)
table.move(表对象, src_start, src_end, 目标索引)
一个区间移动到某个位置,原来位置的自动向前挪或者向后挪
遍历:
以下两种遍历区别在于:
pairs是无序的,每次都可能顺序不一样
ipairs是有序的,但是只能用于那种索引是1、2、3这样的,而且中间不能缺失,否则就会有问题
for k, v in pairs(test_table) do
| for k, v in ipairs(test_table) do
pirnt(k, v)
end
函数
函数声明:
function Fun_name(参数列表)
xxxxxxxxxxxxxxxxxxx
return x1,x2,x3
// 可选的多值返回语句
end
lua的参数大多数是时候都是值传递,只有参数是一个表的时候,其行为像引用传递
调用函数时,参数比函数定义多则丢弃,少则视为nil
对于多返回值的函数调用:
若有function Fun_1 return "Luo1", "Luo2" end
如果左边没有=
来接受返回值,那么所有返回值被丢弃
如果左边a, b, c = Fun_1()
,那么c的值为nil
只有函数第一个返回值有效的情况:
a = Fun_1()
,那么第二个及之后的返回值被丢弃
a, b = Fun_1(), 100
,a的值为"Luo1",b的值为100
return Fun_1(), 100, 200
,返回了"Luo1", 100, 200
所有函数返回值都有效的情况:
a, b = Fun_1()
test_table = {Fun_1()}
return Fun_1()
return 100, 200, Fun_1()
,返回了100, 200, “Luo1”, “Luo2”
只有这个函数调用在表达式的最后一个时,所有返回值才能被正确接收
变长参数:
三个点即代表变长参数:function Fun_name(...)
使用test_table = {...}
这样可以将可变参数列表打包成一个表对象
也可用以下两个函数显示打包或解包可变参数
test_table = table.pack(...)
a, b, c = table.ubpack({...})
IO输入输出
lua有两种IO模型
一种是lua自身提供的输入输出流,称为简单IO模型
另一种是C风格的文件处理,称为完整IO模型
简单IO模型
当前输入流:
进程的标准输入,类似stdin
io.read()
从里面读出来
io.input(文件名)
,用该文件替换标准输入
local old = io.input()
空调用可以返回原来的输入流保存
当前输出流:
进程的标准输出,类似stdout
io.write()
写进去
io.output(文件名)
,用该文件替换标准输出
io.read():
io.read("a", "l", "L", "n")
读有很多模式,a表示整个文件,l表示读一行并丢掉换行符,L表示读一行但保留换行符,n代表读一个数字
io.read(100)
如果直接传一个数字,则是读多少个字符,或者说多少字节
io.read("n", "n", "n")
连续读三个数组
任何一次io.read()
返回了nil,都表示1到了文件尾
io.write():
可以用n多个参数传到write,由wirte拼接:io.write("test", "Luo", "Luo\n")
也可以string.format拼好后整个传给write:io.write(string.format("%s TestLuoLuo %d", "aaa", 100))
完整IO模型
local file = io.open("/etc/xxx", "wr")
,可以像c那样操作文件
成功返回一个流对象
失败返回三个值,第一个是nil,第二个是错误信息,第三个是错误码
local txt = file:read("a")
file:write("xxxx")
file:close()
执行系统命令os.execute("shutdown -r now")
杂
if 条件 then
code
else if 条件 then
code
else
code
end
while 条件 do
code
end
repeat
code
until 条件
for i = 初始值, 目标值, 每次循环增加多少值 do
code
end
goto
goto的标签定义:::tag::
然后goto tag
局部变量的作用域 终止于 最后一个有效句处,而标签的定义是无效语句,也就是说
do
local a
goto tag
local b
::tag::
在这里是看不到a和b的定义的
end
泛型for、迭代器
lua的迭代器,理解成:返回集合中下一个元素的函数
泛型for循环保存3个值:
迭代函数
不可变状态(要遍历的对象)
控制变量(循环计数或者说本次循环的一个标记,下轮循环根据这个标记来找下一个值)
for 变量列表 in 表达式列表 do
end
变量列表:如果变量列表第一个为nil,循环结束,同时也用于保存控制变量
表达式列表:表达式列表需要且只要三个,多余丢弃,不足nil不足,
或者说也不是表达式列表,因为大多数时候此处是个函数,这个函数应该返回三个值
这三个值就是前面的迭代函数 不可变状态 控制变量
如pairs的实现:
local function mypairs(t)
return next, t, nil //next(t, nil),返回表中的第一组kv,第二参数非nil时,根据第二参数随机返回下一组kv
end
以下三个for是等价的:
for k, v in pairs(t) do
for k, v in next, t do
for k, v in next, t, nil do
第一次循环,next(t, nil)返回第一组,控制变量存到k
第二次循环,next(t, k)随机返回下一组
把next替换成自己写的函数,那就是自己的迭代器了
闭包
局部递归函数的定义:
local digui =
function(n)
digui(n-1)
end
编译的时候digui还没有定义,需要
local dogui
digui =
function(n)
digui(n-1)
end
这样就有一个对象,虽然编译的时候还没有值,但是调用的时候有
闭包:
在lua中定义一个局部函数,该局部函数是可以访问其作用域内的其他变量的
do
var = 100
local f = function(a)
print(var)
end
end
但是如果
function createFuncter()
var = 100
local f = function(a)
print(var)
end
return f
end
local funa = createFuncter();
local funb = createFuncter();
这时候funa和funb都能正确访问一个局部的var变量,并且是两个相互独立的var变量
闭包即:一个函数能正确访问 该函数的 非局部变量 的机制
字符串匹配
string.find(在哪个字符串里面查, 查找内容, 从哪个索引位置开始查, 是不是简单查询)
返回两个值,字串的起始索引和结束索引
第四个参数的意思是:string.find(, “[()]”, 1, “”),圆方括号这些字符会用于类似正则的匹配,如果第四个参数传true,说明是一个简单查询,然后就只是单纯匹配“[”这个字符
string.match(在哪个字符串里面查, 类似于正则的表达式详见后)
返回匹配的字符串
string.gsub(在哪个字符串上操作, 类似于正则的表达式详见后, 把匹配的字符串替换成什么, 如果有多个匹配最多替换几次)
如果第三参数是一个函数:那么这个函数应该接受匹配的子串作为参数,返回要替换成的目标字符串
如果第三参数是一个表:使用匹配的子串作为表索引,找到对应的值,这个值就是目标替换的目标字符串
第一个返回值是被替换后的字符串副本
第二个返回值是替换了几次
string.gmatch()
lua中的字符串匹配规则:
特殊字符:().%+=*?[]^$
. 一个点,代表任意字符
%a 字母
%bxy 以x开头,y结尾的字符,’b()‘
表示匹配一对括号之间的东西,包含括号本身
%c 控制字符
%d 数字
%g 除空格外的可打印字符
%l 小写字母
%p 标点符号
%s 空格
%u 大写字母
%w 字符和数字
%x 十六进制数
大写代表补集,如%S表示空格外的所有字符
和前面的某个%组合使用
+ 一次或多次(最长)
* 零次或多次(最长)
- 零次或多次(最短)
? 零次或一次
例子:
%d+/%d+/%d+
用于匹07/09/2000这样的日期,或者%d%d/%d%d/%d%d%d%d
在一对方括号[]
中的称为字符集:[abcdABCD]
,[0-5a-c]
来匹配某些字母或数字,或者[%[%]]
来匹配一对方括号
string.match()
可以返回多个字符串,需要做的是把要返回的字符串用()
括起来:
key, val = string.match(“name = luoluo”, "(%a+)%s*=%s*(%a+)")
print(key, val)
输出:name luoluo
即:在括号内的就打包成一个变量返回
时间
时间有两种格式:
①unix时间戳
②类似struct tm的结构(一个表),称为日期表:{year = 2000, month = 7, day = 10, hour = 15, min = 13, sec = 01, yday = xxx, wday = x, isdst = false }
,实际上在调用对应函数时不是所有成员都是必须的,isdst表示是否为夏令时。
os.time(日期表)
不传递参数,返回当前时间戳
传递一个日期表参数,将该日期表转换成时间戳返回
os.date(“*t”, 可选传递的时间戳)
返回一个格式化的时间或者日期表
os.date(“*t”)
就会返回完整的日期表结构
os.date(“*t”, os.time())
,等价于os.date(“*t”)
os.date(“%Y/%m/%d”, os.time())
返回格式化时间戳 2022/12/28
日期加减计算:
t = os.date(“*t”)
今天是2022/12/28
t.month = t.month + 1
此时表结构大概是{year = 2022, month = 13, day = 28}
t = os.date(“*t”, os.time(t))
才能变成{year = 2023, month = 1, day = 28}
预编译、执行、错误处理
预编译:lua执行代码前预编译生成二进制文件
luac -o MyLuaFile.lc MyLuaFile.lua
将lua文件预编译
lua MyLuuaFile.lc
执行这个预编译好的二进制
loadfile()
预编译代码,但不会执行,只是预编译
出错返回两个值,第一个是nil,第二个是错误信息
dofile()
预编译+执行代码
load()
从字符串或者函数中加载一段代码段,返回一个函数,调用这个函数可以执行代码
出错返回值和loadfile()一样
a = 1
b = 2
fun = load("a = a + b")
fun();
// 执行完a的值为3
注:load只是简单的执行代码,不会像前面的闭包那样每个都不一样的独立变量
因为load总是在全局环境加载代码,所有如果定义了a = 1;local a = 2;
那么load访问的总是全局的那个,相对的,闭包函数则总是访问离他最近的那个定义
错误:
error("错误信息", 2)
,error函数主动引发一个错误
第二可选参数是一个层数,假设调用error所在函数fun()调用是第一层,那么2就代表谁调用了fun()那一层
安全调用:pcall(fun())
不会引发错误
成功返回 true,然后返回 fun()的所有返回值
失败返回false和错误信息
模块
导入一个模块,这个模块可以是lua编写的其他文件,也可是其他语言的lib什么的
require "string"
;然后 string.format()
什么的,来访问这个模块里的函数或者变量
流程:require先在全局package.loaded
检查是否已加载,如果已经加载过,那么就不会再执行一次了,除非把这个模块从package.loaded删掉
没有加载过,从package.path寻找,这也是一个表,包含很多路径,去这些路径里找
先找对应名字的lua文件,找不到才找同名c库什么的
保存到package.loaded
编写一个模块的格式:
local MyModuleName = {}
各种代码往MyModuleName里插函数或者变量什么的
retuern MyModuleName 或者package.loaded["文件名"] = MyModuleName
元表、元方法
元表:可以为表类型定义加减法之类的操作,或者一些默认的访问行为之类的
元方法:存在元表中某个字段的函数
一个表的元表不能直接通过某字段读取,需要通过:
local MyTable = {}
local MyMetaTable = {}
获取一个表的元表:getmetatable(MyTable)
设置一个表的元表:setmetatable(MyTable, MyMetaTable)
如果有:
MyMetaTable.__add = function MyTable(t1, t2) return t1.a + t2.a end
setmetatable(MyTable, MyMetaTable)
那么调用 MyTable + {a = 2}
的时候,就会来找这个__add
对应的函数
寻找的过程先找运算符左边对象元表的__add
,找不到再找右边对象元表的__add
找不到就抛出异常,但是如果右边不是表、没有a这个成员,报错之类的,应该由用户处理
类似__add的还有很多:
__sub 减
__div 除
__floor 除
__unm 负数
__mod 模
__pow 幂
__band 与
__bor 或
__bxor 非
__bnot 反
__shl 左移
__shr 右移
__concat 连接符. .
__eq 等于
__lt 小于,对于表是真子集
__le 小于等于,对于表是子集
__tostring 到字符串
__len #
操作符长度
__metatable 用来保护元表不被修改,若设置了该字段,getmetatable会返回该字段的值,setmetatable则会报错
__index
没有设置该元方法时,访问不存在的元素返回nil,这是默认行为
当设置了该方法,会转而调用该方法返回一个值
MyMetaTable.__index = function (t, key)
return 9999
// 任意都不存在的值都返回999
return OtherTable[key]
// 或者访问另一张表
end
rawget(t, i)
//总是会绕过__index
访问原始数据
如果设置的是一个另一个表:那么使用键访问这个表
__newindex
给索引的赋值时候,如果__newindex
没有被设置,默认行为
如果设置了,不管是函数还是表,都和__index
行为类似
也有rawset(table, key, v)
来绕过
使用元表模拟面向对象行为
冒号运算符:Table:fun1(parm1, parm2)
等价于Table.fun1(Table, parm1, parm2)
冒号运算符总是把冒号左边的对象作为一个参数,隐式传递给冒号右边的函数,作为第一参数
要在函数中访问这个第一参数,使用self
即可,比如return self[parm1]
主要作用于lua模仿继承行为,概念类似于:传递了一个基类还是派生类对象给这个函数,或者说基类还是派生类对象在调用成员函数
模拟类和对象
lua里只要声明了,就是对象
在概念上将其视作对象
在语言本身上,不管是类还是对象,都是对象
local BaseClassDef = {a=15, b = function (self) return self.a end} // 在lua本身是一个对象,但将其视为一个模板、类定义
BaseClassDef:c = function () return self.a + self.a end
local BaseClassObj1
setmeatatable(BaseClassObj1, {__index = BaseClassDef})
类定义是:BaseClassDef
类对象是:BaseClassObj1
访问类对象BaseClassObj1的时候:
BaseClassObj1.a
BaseClassObj1.b()
BaseClassObj1.c()
都是没有的,然后到元表BaseClassDef去找,就能找到了,这些都被封装起来,像是BaseClassObj1有了成员,有了方法
当给a赋值时:
BaseClassObj1.a = BaseClassObj1.a * 100
BaseClassDef 和 BaseClassObj1 就都有一份独立的a,前者是15,后者是1500
BaseClassDef的对象factory:
BaseClassDef:MyFactory(t)
t = t or {}
self.__index = self
setmetatable(t, self)
return t
end
BaseClassObj2 = BaseClassDef:MyFactory()
BaseClassDef:MyFactory内部先生成一个空表对象
接下来一行看起来并没用,等后面继承补
setmetatable给新的对象设置元表,这样就能访问数据成员或者成员方法了
然后返回一个BaseClassDef的对象BaseClassObj2
模拟继承
BaseClassDef:MyFactory(t)
t = t or {}
self.__index = self
setmetatable(t, self)
return t
end
DeviceClassDef = BaseClassDef:MyFactory() // 这样派生类的定义就可以了,接下来往里面放东西就行了
function DeviceClassDef:fun1(parm1) ...code... end
function fun2(DeviceClassobj, parm1) ...code... end
DeviceClassObj1 = DeviceClassDef:MyFactory() // 派生类对象有了
重点在于MyFactory的self.__index = self; setmetatable(t, self)这两句
调用BaseClassDef:MyFactory()时:
setmetatable(t, self)把BaseClassDef作为元表设置到了新对象、新类DeviceClassDef中
那么DeviceClassDef访问不存在的元素 -》 检查有没有元表 -》 有元表 -》 找__index -》 __index指向表BaseClassDef -》 尝试在BaseClassDef中找一个元素
调用DeviceClassDef:MyFactory()时:
setmetatable(t, self)把BaseClassDef作为元表设置到了新对象DeviceClassObj1中
当DeviceClassObj1.a时:试图访问不存在的元素 -》 找元表的__index -》 指向表DeviceClassDef -》 a在DeviceClassDef也是不存在的元素 -》 转到上面 -》 最终在BaseClassDef中寻找
多重继承
懒得写了,到处都是
环境
一个普通的表(全局表):_G
_G[“aaa”]直接访问
_G略有特殊:不允许值为nil,判断有没有某个值要用rawget(_G, “aaa”) != nil
自由名称:可以理解成没有带local的变量
全局变量是假的,其行为用特殊变量名_ENV来模拟
预编译代码前,在所有代码(一个文件内)外层创建local _E在这里插入代码片
NV
所有自由名称xxx替换为_ENV.xxx
load全局初始化代码段的第一个上值?不太能理解,也许是继承lua运行环境的某个表里的所有值,print之类的东西
local a
b = c + a
类似于
local _ENV = xxx
--print什么的复制过来?
local a
_ENV.b = _ENV.c + a
a = 1
local a = 2
_ENV.a
、_G.a
的值都是1
大多数时候_ENV、_G指向同一个表,但是_G不管什么时候都是唯一一份,而_ENV在代码上下文中是可变的
给_ENV赋值一个新表需要注意,因为print之类的在里面,_ENV = {}
之后,就不能访问print了,但是_G.print
还是可以的
a = 1
_ENV = {g = _G}
// 改变了全局的_ENV
// 如果这里是local _ENV
那么接下来作用域内的所有自由名称都被这个local _ENV捕获
超出作用域又回归全局的_ENV捕获
// function (_ENV) end _ENV这个特殊名字在函数也有同样的效果
a = 2
g.print(1+2)
// 在_EVN中的值就像被声明过一样,可以直接用
_ENV.a
的值是2
_G.a
的值是1
垃圾回收
local对象超出作用域被回收,通过collectgarbage("count")
查看内存占用
当一个对象被放入表 声明为全局对象,他就没有办法被回收了
但是弱引用表除外
表是否为弱引用取决于元表的__mode字段,其值为“k”、“v”、“kv”
之一
分别代表键是弱引用还是值是弱引用,还是两者都是
如果一个对象只被一个弱引用表持有(即引用计数1,且这个1来自弱引用表),那么他就会被回收。
k代表键是弱引用时被回收,kv代表键值任意一个弱引用时被回收,任何一个键值被回收了,整个键值都会从表中删除。
析构:
通过元表__gc
给对象设置一个回收时调用函数
但是需要注意,必须在调用setmetatable时元表里有__gc
,setmetatable
时没有,但后续又给元表添加了__gc
方法,是不会调用这个函数的
按setmetatable
的逆序调用他们的析构
进入析构后就被标记为已析构,只是标记,没有真删
复苏:
如果一个对象的析构访问了自己,那么这个对象会复苏,即放到全局变量中
并且复苏是能传递的,析构访问了其他对象,那么其他对象也会被复苏
collectgarbage():
第一个参数是字符串,控制功能
第二个参数是某些功能需要一些数据,可选项
“collect”:默认,回收一下垃圾
“stop”:停止垃圾收集。
“restart”:重启垃圾收集。
“count”:返回已使用内存数,包括死掉但还没被回收的对象。
“step”:大概是分配多少字节后来一次垃圾回收?使用第二参数。
“setpause”:每隔多久来一次垃圾回收,第二参数是百分比,0%~200%,值越小频率越高。
“setstepmul”:看不懂干嘛的,默认200%,低于100%会让垃圾回收效率低。
协程
协程四状态:挂起、运行、正常、死亡
fun = function () coroutline.yield() end
thread1 = coroutline.create()
返回一个thread类型的值
coroutline.status(thread1)
返回协程的状态,新创建的协程默认挂起
coroutline.resume(thread1)
把协程唤醒,开始或继续执行
coroutline.yield()
遇到这个调用会把协程挂起,直到再次调用coroutline.resume(thread1)
coroutline.resume(thread1, parm1, parm2, parm3..)
额外的参数作为fun的参数,或者正在等待的yield的返回值
第一个返回值是bool, true表示没有错,
后面的返回值是yield的参数,resume之后才开始调用yield(parm3, parm4…),参数是多少,这里返回值就是多少。
如果协程结束,这里的返回值就是fun的返回值
C中调用lua
三个头文件:<lua.h>、<luaxlib.n>、<lualib.h>,还有库文件链接
大概流程
lua_State* L = luaL_newstate();
lua和c的交互都基于lua_State对象,
luaL_openlibs(L);
新对象没有任何状态和预定义函数,该调用将print之类的函数加载进来
luaL_loadString(L, 某个缓冲区);
从缓冲区读点数据到栈里
lua_pcall(L, 0, 0, 0)
调用一个lua函数,见后
lua_pop(L, 1)
从栈顶弹出几个
lua_close(L)
lua_State是一个栈,提供一组函数来向lua_State中压入数据:
第一个压入栈的数据索引为1,然后2、3,-1表示栈顶索引,-2表示栈顶下面那个数据的索引
很多操作lua_State的函数都要依赖该索引,另外进入栈的数据全部都是原来的副本
void lua_pushnil (lua_State * L);
void lua_pushboolean (lua_State * L, int bool);
void lua_pushnumber (lua_State * L, lua_Number n);
double类型
void lua_pushinteger (lua_State * L, lua_Integer n);
64位int
void lua_pushlstring (lua_State * L, const char * s, size_t len);
字符串包含\0之类的用这个
void lua_pushstring (lua_State * L, const char * s);
。
一组函数签名相同的lua函数,用来检查某个栈索引上的数据类型,星号代表各种数据类型:
lua_is* (lua_State * L, int index);
一组参数类似但返回值不同的转型函数:
lua_to*(lua_State * L, int index);
lua_tostring(lua_State * L, int index, size_t * len);
第三参数是返回了多少字节
lua_Number lua_tonumberx (lua_State * L, int index, int* isnum);
第三参数是是否成功
lua_Integer lua_tointegerx (lua_State * L, int index, int* isnum);
操作栈的其他函数:
int lua_gettpo (lua_State * L);
返回栈顶索引,也是元素个数
void lua_settop (lua_State * L, int index);
调整栈的高度,比原来高出的nil填补,比原来低出的丢掉
void lua_pushvalue (lua_State * L, int index);
把索引index上的副本再一次压入栈顶
void lua_rotate (lua_State * L, int index, int n);
看不懂
void lua_romove (lua_State * L, int index);
void lua_insert (lua_State * L, int index);
void lua_replace (lua_State * L, int index);
index上的数据的直接弹出,栈顶数据的直接移到这
void lua_copy (lua_State * L, int startIndex, int endIndex);
获取和设置全局变量
lua_getglobal(lua_State * L, const char *)
获取某个全局变量,然后压入栈里
lua_setglobal (lua_State *L, const char *name)
弹出一个栈顶的值并写到lua全局变量name中
在c中,不管是调用函数、操作lua表对象,流程大致一样:
①把要函数名、lua表对象压入栈
②把函数参数、表的键压入栈
③调用lua_pcall或者lua_gettable之类的
获取表的值
lua_getglobal(L, "table1")
获取lua全局变量table1压入栈,他应当是一个表
lua_pushstring(L, str_key)
压入键,这个table对象索引变成-2
lua_gettable(L, -2)
从-2索引这个table对象上操作,使用索引-1的作为键,取到值后把-1索引的键移除,最后把获得的数据又压入L
lua_pop(L, 1)
从栈顶弹出获得数据,栈重新回归高度为1,内部只存了一个table对象的状态
设置表的值
lua_getglobal(L, "table2")
获取lua全局变量table2压入栈,他应当是一个表
lua_pushstring(L, str_key)
压入键
lua_pushstring(L, 155)
压入值
lua_settable(L, -3)
压入键值后表在栈中的索引变为-3,写入键值后,键值被自动从栈中移除,只剩下表在栈中
调用函数
lua_getglobal(L, "fun1")
获取lua全局变量fun1压入栈,他应当是一个函数
lua_pushnumber(L, 1)
压入函数参数
lua_pushnumber(L, 2)
压入函数参数
lua_pushnumber(L, 3)
压入函数参数
if(lua_pcall(L, 3, 1, 0) != LUA_OK)
表示从L中调用函数,函数有3个参数,1个返回值
处理错误;
调用函数完成后,栈被清空,然后将0或多个返回值压入栈中