读 lua编程设计

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/tran119/article/details/82504365

疑问+提炼,将自己的疑问都记录下来,将书上有必要写的,记录的东西提炼出来。其他不记录。
疑问:
lua的解释器是一个简单的应用程序,依靠lua的库来实现主要功能。(猜想,lua会向lua的虚拟机申请内存,虚拟机会开辟一大块的内存),在程序里面,硬件,操作系统,语言都各自有自己的堆栈概念,一条赋值语句就是一条指令而已,只有产生内存才会在堆栈存在。还是不太清楚,先放在这里。???
3,LUA_INIT?
4,非局部变量在什么时候销毁?说法:应该是非局部变量的作用域是在内部函数作用域结束时,即没有指向时。这个应该是错误的,因为一个函数也是存在于表中,而闭合函数返回的函数也是存在于变量中,只有变量执行才会执行,所以只能消耗当前的模块,才可以把所有的指向抹掉,才会被垃圾回收。
5,package.preload是什么??


提炼:
1,阶乘
function fact(n)
  if n <= 0 then
    return 1
  end
  return n*fact(n-1) --当执行到这一步时,n被保存下来,函数结束,执行下一个函数
end
2,lua中,所谓的加载程序库,其实就是加载lua文件如test.lua,也就是加载模块。
3,lua中,以_开头的,后接一个,或者多个大写字母的变量,一般都是特殊标识符,应该避免,如:_VERSION
4,将全局变量设置为nil就可以删除该全局变量。全局变量如果没有设置为空,那将一直存在,因为存在_G表的引用。
5,解释器会查找一个名为LUA_INIT的环境变量,并先运行该变量的内容,如果内容为“@文件名”,则执行该文件,如果为字符串,则执行字符串代码。这样就可以达到,预先加载程序包,定义函数,修改函数名字等操作。
6,解释器在运行脚本前,会创建一个名为arg的table,并且将脚本的名称,参数,执行的操作命令放置在该表中。脚本名称在0号索引,1,2号为参数1,参数2。-1,-2等为操作命令。比如:print(arg[0],arg[-1]) --E:\Lua\test0.lua io.stdout:setvbuf('no')
7,lua将nil和false视为假,其他都为真,比如0,“”都为真。
8,lua字符串是不可变得值,比如:a="a" b= a b="b" print(a,b)--a    b
9,table是关联数组,可以将它理解为一个动态分配的对象,它的key可以是除了nil之外的所有类型。比如:使用数字,字符串,函数(可以运行该函数),表,作为key。
myTable2 = {} function test() end str = "str" num = 10
myTable = {[num]=1,[str]=2,[test]=3,[myTable2]=4}
for key,value in pairs(myTable) do
  print(type(key),value)
end
--[[ 打印结果如下:function    3 table    4 number    1 string    2 ]]
表是通过构造表达式创建的,比如{}。
function test()
  local tmpList = {[1]=1,[2]=2}
  return tmpList
end
--建议加上这行代码,这样就知道返回的是一个表,更加直观,myTable = {}
myTable = test()
print(type(myTable)) --table
--table永远是匿名的(没有名字),持有表的变量和表本身没有固定的关联性
a = {[1]=1,[2]=2} b = a b = {}
print(#a,#b)--2    0 这个时候的b已经是另外一个表了,和a没有关系了
a = {[1]=1,[2]=2} b = a b[3] = 3
print(#a,#b)--3    3 b仍然和a指向同一个表
a = {[1]=1,[2]=2,[3]=3} a[2]=nil
print(#a)--1 这个时候表其实有2个元素,必须使用pairs函数

10,lua所有的标准库都是用C语言写的,包括对字符串的操作,table的操作,操作系统功能调用,数学函数等。
11,取模,x%1=小数部分,x-x%1=整数部分,x%0.01=小数后3位的值,如果单独打印的话,很难看出来,很难理解。x-x%0.01=x保留后2位小数点的值,比如:x=1.11115,那结果为1.11,x%0.01=0.00115(如果单独打印,值不为这个)。
12,对于table,userdata,函数,比较引用,即是否引用于同一个对象。大小比较只能是2个数字或2个字符串,其他都会报错。
13,and or都短路求值,即需要时才判断第2个参数。常用写法:type(v)=="table" and v.tag == "h1" ,可以保证不会出错。另一种写法,我个人不推荐,感觉不明确,(a and b) or c相当于其他语言的a?b:c。
14,构造table时,可以有3种方式,a={x=1,[2]=2,["3"]=3},第2个是number,其他为string。
15,常用写法,local a = a 创建局部变量a保存全局变量a的值,如果不存在该全局变量a,那局部变量a的值为nil。作者建议的写法是,在需要的地方声明局部变量,这样局部变量在声明时就给人一种可以理解的意义。
16,与其他大多数语言不同的是,在循环体中的局部变量的作用域包括了条件测试。如下:
repeat
  print('xxx')
  local a= nil
until a==nil --条件判断中,依旧可以拿到局部变量a
17,for循环会默认将控制变量设置为局部变量,且无法通过修改变量值来达到控制循环的目的。
for i=1 ,3 do --默认为局部变量
  print(i) --打印1,2,3
  i=i+10 --无法修改局部变量索引i的值
end
print(i)--打印nil
18,逆向的table,有时可以派上用场,a={[1]="one",[2]="two"} 在ipairs函数中用另一个表存储为b={["one"]=1,["two"]=2},这样就可以通过字符串来取到数字。
19,如果函数只有一个参数,且参数为table或者字符串,则调用函数时,可以省略()。比如:print("xx")==print "xx" dofile "xxx.lua" == dofile("xxx.lua") type({x=10}) == type{x=10}  
20,形参为函数的参数,实参为调用函数时传递的参数实际值。默认多就舍弃,少就补nil。
function test(a,b) --a,b为形参
end
test(a) --a为实参,多就舍弃,少就补nil
默认实参的函数,一个全局计数器:
function addprama(n)
  n = n or 1
  count = count + n
  print(count) --1
end
21,函数的返回值数量会在不同情况下自动调整。有如下函数:
function test0() end --返回nil
function test1() return 1 end
function test2() return 1,2 end
function test3() return 1,2,3 end
当单独调用函数时,返回值会全部舍弃,比如:test3()。
函数调用只有作为最后一个元素时,才会返回所有返回值。一共4种情况:
多重赋值:a,b = test3() --3被舍弃
作为参数:print(test3()) --全部传入
table构造式:a = {10,test3()} --全部传入,表a为{10,1,2,3}
return语句:return 10,test3() --全部传入,返回10,1,2,3
如果函数调用不是作为最后一个元素时,只会返回一个参数。如下:
多重赋值:a,b = test3(),10 --2,3被舍弃,a,b=test0(),10--nil,10
作为参数:print(test3(),10) --2,3被舍弃,print(test3()..10)--110
table构造式:a = {test3(),10} --2,3被舍弃,表a为{1,10}
return语句:return test3(),10 --打印1,10
如果用(test3())这种写法,那一定是返回一个值。
return (test2(),test3()) --这种写法直接报错
return test2(),test3() --打印1    1    2    3,test3()全部返回,test2()只返回一个参数
22,unpack()函数
a = {1,2,3} --unpack(a)相当于return 1,2,3
print(unpack(a)) --打印1,2,3
function myUnpack(t,i)
  i = i or 1
  if t[i] then
    return t[i],myUnpack(t,i+1)
  end
end
23,可以使用{...}遍历变长参数,书中例子说可以用ipairs(...)遍历,试过之后报错。变长参数作为函数参数时,必须放在最后一位。
function test(...)
  a={...} --使用表的写法来访问变成参数
  for i=1,#a do
    print(a[i])
  end
  --for k,v in ipairs(...) do --报错
  --  print(k,v)
  --end
end
变成参数中可能会传入一些nil值,这时需要select函数。select函数的参数只能为数字或者"#","#"返回变长参数的总数,包含nil值,数字返回变长参数从当前索引到最后索引的值。
function test(...)
  for i=1,select("#",...) do
    print(select(i,...))
  end
end
test(1,2,nil,4) --打印如下:
1    2    nil    4
2    nil    4
nil    4
4
24,在lua中,参数具有位置性,实参通过参数表中的位置与形参匹配。具名实参,一种使用一个参数,且参数为表,从而可以省略(),而直接使用表让参数更加有阅读性的一种方法。比如:test({})函数,test{name="xxx.xx"},更加有阅读性。该书举了一个GUI库创建窗口的例子,如下:
function window(t)
  if type(t.width)~="number"then --1,检查必填参数
    error("no width")
  end
  --2,可选参数默认初始化
  _window(t.x or 0,t.y or 0,t.width,t.height)--需要调用的真正的函数
end
window{x=1,y=1,width=300,height=200} --调用函数
25,lua中的函数是第一类值,具有特定的词法域,第一类值:函数就像数字,字符串一样,可以存在变量,table,作为参数,作为返回值。词法域:函数可以嵌套在另一个函数中,内部的函数可以访问外部函数的变量,即闭包。在lua中,函数与值一样是匿名的,即没有名称,讨论一个函数其实是在讨论持有该函数的变量。一个函数定义其实就是一条赋值语句,该语句创建了一种类型为函数的值。
比如:function test() end 其实就是所谓的语法糖,真正的操作是test = function () end
function () end 相当于函数构造式。
26,高阶函数:使用匿名函数,或者函数作为参数的函数,被称为高阶函数。
27,闭合函数,在lua中,只存在闭合函数,而不存在函数,函数是闭合函数的一种特殊情况,即没有使用非局部变量的闭合函数。非局部变量:一个函数中的局部变量被该函数内部的匿名函数访问的变量。如下:
function test()
  local i = 0
  return function() i = i + 1 return i end
end
print(test()) --function: 0x00691a10,会返回内部的函数
print(test()()) --打印1,拿到内部的返回函数,并执行内部返回的函数
print(test()()) --打印1,拿到内部的返回函数,并执行内部返回的函数
f = test() --拿到内部的返回函数
print(f()) --打印1,执行内部函数,发现打印还是1,说明非局部变量的作用域是在内部函数作用域结束时,即没有指向时。
print(f()) --打印2
f2 = test() --拿到内部的返回函数,重新创建了另一个闭合函数,使用另一个非局部变量。与f没有关系。
print(f2()) --打印1
print(f()) --打印3
居于上面的特性,可以有这种应用,比如,当点击一个按钮时,传入一个参数,然后根据该参数,在需要时执行点击按钮的回调函数,但是函数已经执行完毕,所以传入的参数本来已经被销毁了,但是闭合函数保留了下了。如下:
function buttonclick(i)
  return function() return i end --拿到返回的回调函数,在需要时执行执行回调函数时,依旧可以使用点击按钮之前的参数
end
28,因为函数可以赋值给变量,所以可以重新实现该函数,以此创建一个沙盒。比如从网络上接到代码,该代码在接收完毕之后需要调用一些系统的函数,可是这些代码不确定是否是恶意的。如下:
old = io.open --拿到原本的函数
local access_OK = function(filename,mode) end --检查是否恶意
io.open = function(filename,mode)
  if access_OK(filename,mode) then
    old(filename,mode) --调用原本的函数
  else
    print("error")
  end
end
相对于提供一整套大而全面的解决方案,lua提供的则是一套元机制,可以根据特定的安全需求来创建一个安全的运行环境。
29,大部分的lua库都是将函数存放在table中,比如:io.read,math.sin。只要有函数的地址,加个()就可以调用。
30,lua将每个程序块作为一个函数来处理。程序块:一连串的语句或者命令,比如:a=1,b=2。
31,局部函数,如下:
local test = function(i) 
  if i == 0 then 
    return 1 
  else 
    return i*test(i-1) --报错,因为test此时并没有定义完成,这里调用的是全局变量test
  end
end
local test
test= function(i) 
  if i == 0 then 
    return 1 
  else 
    return i*test(i-1) --正确,因为提前定义了局部变量test,虽然函数定义没有完成,但在执行函数时,test早就完成了定义
  end
end
对于局部函数的语法糖,比如:local function test() end,展开为:
local test --所以递归不会出错
test= function() end
对于间接递归,就只能提前声明变量。比如:
local test,test2
function test() test2() end --如果没有声明,那此时test2此时并不存在
function test2() test() end
不要找虐,不要强迫症,跳过。
32,尾调用:一个函数最后一步的调用为另一个函数的调用,会跳出该函数,所以一定是return xx,lua中这种调用不会开辟额外的栈,相当于goto语句。比如:
function test()
  return test2()
end
以下都不是尾调用:return test2()+1; return (test2()) ; return (test2()) 因为都有额外的操作,所以会开辟栈。

33,泛型for迭代器,内部实现使用了闭合函数。
34,lua允许在运行源代码之前,先将源代码预编译为一种中间形式,lua仍然被称为解释型语言,解释型语言的主要特征是,编译器是否是语言运行时库的一部分,即是否有能力轻易的执行动态生成的代码,可以说正是因为存储了诸如dofile这样的函数,才可以将Lua称为一种解释型的语言。
35,dofile函数是一种内置的操作,用于运行lua代码,实际上调用了loadfile函数。loadfile加载一个lua文件,编译代码,但不会运行代码,将编译后的代码作为一个函数返回,不会引发错误。因为返回的是函数,所以只需要编译一次代码就可以多次使用。
function dofile(filename)
  return assert(loadfile(filename))
end
36,loadstring与loadfile相似,loadstring读取字符串,每次调用都需要重新编译代码,且loadstring没有词法域的概念,总是在全局环境中编译字符串,所以会取全局环境中的变量。用处是执行程序之外的代码,一般暂时找不到更好的方法解决某个问题。从下面的例子可以看出,只要拿到loadstring的返回值,也是可以多次调用而不必每次都编译的。只是如果只是使用loadstring的话需要每次都编译,而loadfile因为内部做了处理,所以不用。
i=10
local i = 0
f = loadstring("i=i+1;print(i)")
f() --11
g = function() i=i+1;print(i) end
g() --1
37,原始函数load,当程序块不在文件中,或者程序块过大而无法放入内存时,一般会使用load函数。load通过接收一个读取器函数,并在内部调用它来获取程序块,读取器函数可以分几次返回一个程序块,load会反复调用它,直到返回nil。常见的误解是,认为加载了程序块就是定义了其中的函数,其实是运行时才定义了函数。实际应用中,加载程序块应该报告相应的错误,如果代码不可信,还应该放在一个安全的环境中。如下:
f = dofile("test123.lua")
f() --这个时候才定义了函数test
test()
38,C代码需要链接入一个应用程序才可以使用,因为标准C不支持动态链接,所以是不可移植的。lua是由C实现的,一般只会包含由标准C实现的机制。因为动态链接相当于其他机制的母机制,所以可以动态的加载不在lua中的机制。使得lua可以移植,并标准实现支持了windows,linux等平台。使用require函数来加载程序库,函数会搜索指定的库,然后调用loadlib来加载库,并将函数注册到lua。lua的所有的动态链接的功能都集中在package.loadlib函数中。函数有2个参数,库的路径,要加载的库中的函数的名字。如下:
local f = package.loadlib("/xx/xx/xx.so","luaopen_socket")
39,写数据比读数据简单的多,因为写数据有完全的控制权,而读数据却不知道会读到什么内容。所以一个好的程序不仅要能读,还应该可以处理损坏的文件。lua可以使用table来定义一种文件格式,在写数据时为读数据做一些准备,将数据作为lua代码来输出,使得读数据时直接执行lua代码就行。
40,lua中的每个值都有一个元表,table和userdata可以有独立的元表,其他类型的值共享该类型所属的单一元表。比如tostring函数对应于__tostring元方法。函数setmetatable和getmetatable也会用到元表的一个字段用于保护元表,比如某个元表不希望被修改,只需要为该元表设置__metatable字段,getmetatable时返回该字段的值,setmetatable时,会出错。如下:
local myTable = {}
local myMetaTable = {}
myMetaTable.__metatable = "dont change"
setmetatable(myTable,myMetaTable)
print(getmetatable(myTable)) --dont change
setmetatable(myTable,{}) --error
41,有2种可以改变表的行为的方法:访问表,修改表中不存在的字段。分别对应__index,__newindex元方法。使用__newindex还可以创建一个只读表,即在修改不存在的字段时,在元方法中返回一个error就行。
42,lua将所有的全局变量都放在一个table中,这个table被称为环境,且将这个table保存在一个全局变量_G中,_G._G=_G。
43,rawset(table, key, value) rawget(table, key) raw方法就是忽略table对应的metatable,绕过metatable的行为约束,强制对原始表进行一次原始的操作,也就是一次不考虑元表的简单更新。另外,一次原始的操作其实并不会加速代码执行的速度,效率一样。当操作table时,如果我们有以下需求:访问时,不想从 __index 对应的元方法中查询值。更新时,不想执行 __newindex 对应的元方法。在 __newindex 元方法中,设置table的key/value时,不想陷入死循环而爆栈那么,我们可以考虑使用raw方法。注意,raw只是绕过了元方法,但是它操作的还是这个表。如下:
myTable= {}  
mt = {}  
function myTable.new(o)  
    setmetatable(o ,mt)  
    return o  
end
mt.__index = function (t ,key)  
    return 1000  
end 
mt.__newindex = function (table ,key ,value)  
    if key == "num" then  
      --table.num = "2000"  --会直接报错
      rawset(table ,"num" ,"2000")
    end  
end 

table1 = myTable.new({})
print(table1.num) --1000,访问变量,执行__index,返回1000
print(rawget(table1 ,table1.num)) --nil,绕过元方法,变量本身不存在,所以为nil
table1.num = "3000" --为表添加一个新的变量,访问__newindex元方法,并为key设置值
print(table1.num) --2000
rawset(table1,"num","123")
print(table1.num) --123,绕过元方法为表添加一个新的变量
44,一个package包就是一系列的模块。使用require便得到了一个全局变量,变量指向一个table,table的内容就是模块的函数,变量。如下:
test123.lua文件
local myTable = {} --注意这里是局部表,如果是全局表则默认就被放到了_G中
myTable.applenum = 100
function myTable.testPrint()
    print("add")
end
return myTable --必须返回该表,且_G拿不到
test.lua文件
local myTable = require "test123"
myTable.testPrint() --add
_G.myTable.testPrint() --error
_G["myTable"].testPrint() --error
45,几个加载函数的区别
require:内部由package.loaded和fildloader实现,加载一次就会保存,再次加载直接返回原先保存的值。
package.loaded:一个保存了模块的表。
fildloader:通过该函数拿到一个加载器,比如:如果为lua文件就返回loadfile或者为C库就返回loadlib。
loadfile:加载编译模块为中间代码,但不运行代码。加载lua文件。与loadstring功能相似。
loadlib:加载编译模块为中间代码,但不运行代码。加载C程序库。
代码如下:
function myRequire(name)
  if not package.loaded[name] then --如果已经加载就直接返回已经加载过得模块
    local loader = findloader(name) --没有加载则找一个适合的加载器,比如loadfile
    if loader == nil then --找不到文件就报错
      error("unable to load module "..name)
    end
    package.loaded[name] = true --防止模块加载的死循环,有点难理解,暂时不考虑??
    local res = loader(name) ----如果为local表,且没有返回,这里的res会为空
    if res then
      package.loaded[name] = res
    end
  end
  return package.loaded[name]
end
--如果想对同一个模块加载2次,如下:
package.loaded["test123"] = nil --杀掉该模块
require "test123" --重新加载该模块
另外44中的例子可以这样:
local myTable = require "test123"
myTable.testPrint() --add
package.loaded["test123"].testPrint() --add
如果44中的例子的test123.lua文件没有返回局部表,所以返回为空表,但是package.loaded[name] = true 。所以结果如下:
local myTable = require "test123" --require时就会返回package.loaded["test123"]的结果
myTable.testPrint() ----attempt to index field 'test123' (a boolean value)
package.loaded["test123"].testPrint() --attempt to index field 'test123' (a boolean value)
46,require的路径中每项都是将模块名转化为文件名,require会用模块名来替换?来查找是否存在文件,路径使用;分隔。require的lua文件的路径放在变量package.path中,lua启动时使用LUA_PATH来初始化该变量。如果没有LUA_PATH就使用编译时定义的默认路径来初始化。在LUA_PATH字符串中,将;;转换成编译时定义的默认路径。如果不存在lua文件,那就去找C程序库,使用package.cpath和LUA_CPATH,正常情况会导出一个名为luaopen_模块名的函数。如果模块名称冲突,则使用模块名_xx的方式解决冲突,require会默认取_后面的xx作为返回的函数名称,即luaopen_xx。比如file_a,file_b,2个模块对应的返回函数名为luaopen_a和luaopen_b。
47,如下写法如果是局部表也可以不用返回该表,require时因为存在,所以直接返回。
local modname = "xx"
local myTable = {}
_G[modname] = myTable
package.loaded[modname] = M
48,猜想,setfenv(1,xx)函数其实就是将该模块存在一个表中,然后_G指向该表。使用setfenv之后,不论你写什么都会被默认成新环境的一部分,所以_G被当成新环境的一个变量,print函数被当成新环境的一个已经定义的函数,。
49,module创建模块时,如果已经存在该模块就直接复用,不存在就创建,所以可以使用module打开一个已经创建的模块。
50,require一个模块xx.xx时,其实就是找package.loaded和package.preload表中的key为"xx.xx"(这是一个字符串)。但是,搜索文件时,xx.xx中.一般是目录分隔符/。
51,将__index元方法赋予一个函数,函数内通过key来索引不同的表,模拟多重继承。
52,在lua中没有提供私密性机制,对象中的变量可以任意操作。
53,使用__mode来创建弱引用table,__mode = "k"表示key为弱引用,"v"表示value为弱引用,用于垃圾回收机制。暂时不看。
------------------------- 交互 ------------------------
54,lua是嵌入式语言,也是可扩展语言。通过虚拟机和解释器(一个简单的应用程序),动态的将lua文件或字符串变成指令,通过lua库(由C语言编写),完成交互。所以lua不是一个单独运行的程序,而是一个可以链接到其他程序的库,通过链接将lua的功能合并入这些程序。lua与C通过一个虚拟栈完成通信,函数间的相互操作都是通过这个栈上的值。
55,压入函数,将其他语言中的值压入对应得lua虚拟栈,供lua代码使用,函数如下:
void lua_pushnil(lua_State *L);
void lua_pushboolean(lua_State *L,int bool);
void lua_pushnumber(lua_State *L,lua_Number n);
void lua_pushinterger(lua_State *L,lua_Interger n);
void lua_pushlstring(lua_State *L,const char *s,size_t len);
void lua_pushstring(lua_State *L,const char *s);
检查从lua到其他语言的参数的函数,如下:
--lua_isnumber检查是否可以转换为数字类型,lua_isstring也一样,所以lua_isstring对于任意数字都返回真。
bool lua_isnil(IntPtr luaState, int index);
bool lua_isnumber(IntPtr luaState, int index);
bool lua_isboolean(IntPtr luaState, int index);
从lua虚拟栈中获取值到某种语言的函数,如下:
string lua_tostring(IntPtr luaState, int index);
double lua_tonumber(IntPtr luaState, int index);
bool lua_toboolean(IntPtr luaState, int index);
IntPtr lua_tocfunction(IntPtr luaState, int index);
int lua_tothread(IntPtr L, int index);
IntPtr lua_touserdata(IntPtr luaState, int index);
56,API使用索引来引用栈中的元素,以栈底为参考,第1个入栈的被称为栈底,索引为1,后面依次类推为2,3,4。如果以栈顶为参考,-1为栈顶元素,依次类推,-2为倒数第2个入栈的元素。操作栈的函数如下:
int lua_gettop(IntPtr luaState);--返回栈中元素个数,也就是栈顶元素索引
void lua_settop(IntPtr luaState, int newTop);--设置栈顶元素索引,高则删除,低则补nil,设置为0就是清空栈。也可以使用负数索引来操作。
void lua_pushvalue(IntPtr luaState, int index);--将该索引上的值复制一份,放到栈顶,也就压入栈
void lua_remove(IntPtr luaState, int index);--删除指定索引元素,同时下移其他元素
void lua_insert(IntPtr luaState, int newTop);--先上移其他元素,将栈顶元素的值弹出,也就是删除,然后将值放到索引位置
void lua_replace(IntPtr luaState, int index);--将栈顶元素的值弹出,也就是删除,然后替换索引对应得值
57,每个函数都有自己的局部私有栈。
lua_pushfunction(L,test);--将自定义的C函数压入栈
lua_setglobal(L,"test");--将该函数赋予全局变量test
int lua_pcall(IntPtr luaState, int nArgs, int nResults, int errfunc); --参数为:栈指针,参数数量,期望的结果,错误处理函数的索引。调用lua中的函数。
lua_getglobal(L, "var"); //变量的值现在栈顶
int var = lua_tonumber(L, -1);
--需要弹出的数量,填1就是-2,填2就是-3
public static void lua_pop(IntPtr luaState, int amount)
{
    LuaDLL.lua_settop(luaState, -(amount) - 1);
}
void lua_close(IntPtr luaState);--关闭某个虚拟栈
常用情况:
[MonoPInvokeCallbackAttribute(typeof(LuaCSFunction))]
public static int CS_XXXFun(IntPtr L)
{
    float startX = (float)LuaDLL.lua_tonumber(L, 1);
    float startZ = (float)LuaDLL.lua_tonumber(L, 2);
    float endX = (float)LuaDLL.lua_tonumber(L, 3);
    float endZ = (float)LuaDLL.lua_tonumber(L, 4);
    bool bRet = GASceneManager.Instance.XXXFun(startX, startZ, ref endX, ref endZ);
    LuaDLL.lua_pushboolean(L, bRet);
    LuaDLL.lua_pushnumber(L, (double)endX);
    LuaDLL.lua_pushnumber(L, (double)endZ);
    return 3;
}
先暂时到这里吧

 

猜你喜欢

转载自blog.csdn.net/tran119/article/details/82504365