C与Lua互相调用的时候,栈变化分析
1 C调用Lua函数的堆栈变化
例子
Lua文件中的函数 function testNewCounter2() return "第四个结果" end C中的例子 void t_new(lua_State *aaa){ } lua_pushstring(aaa, "feifei"); lua_pushcfunction(aaa, t_new); const char *ccc= lua_tostring(aaa, -2); printf("倒数第二个的值为%s\n",ccc); lua_getglobal(aaa, "testNewCounter2"); int iiiiif=lua_gettop(aaa); printf("现在栈内得数目=%d\n",iiiiif); lua_pcall(aaa, 0, 1, 0); int iiiii=lua_gettop(aaa); printf("执行函数之后现在栈内得数目=%d\n",iiiii); const char *ccfffc= lua_tostring(aaa, -3);//倒数第三个 const char *ccfffc2= lua_tostring(aaa, -1);//倒数第一个,也就是lua函数的返回值 printf("倒数第三个=%s\n",ccfffc); printf("倒数第一个,也就是lua函数的返回值=%s\n",ccfffc2);
运行结果如下:
倒数第二个的值为feifei
现在栈内得数目=3
执行函数之后现在栈内得数目=3
倒数第三个=feifei
倒数第一个,也就是lua函数的返回值=第四个结果
也就是说函数执行完了之后,首先是把自身弹出,然后把返回结果压住栈内,
而在函数压入之前的,比如feifei等,函数控制不了,也不会把他们弹出
下面给lua函数加一个参数试试,也就是lua文件的函数为
function testNewCounter2(para) return "第四个结果” .. para end lua_pushstring(aaa, "feifei"); lua_pushcfunction(aaa, t_new); const char *ccc= lua_tostring(aaa, -2); printf("倒数第二个的值为%s\n",ccc); lua_getglobal(aaa, "testNewCounter2"); lua_pushstring(aaa, "lua的参数"); int iiiiif=lua_gettop(aaa); printf("现在栈内得数目=%d\n",iiiiif); lua_pcall(aaa, 1, 1, 0); int iiiii=lua_gettop(aaa); printf("执行函数之后现在栈内得数目=%d\n",iiiii); const char *ccfffc= lua_tostring(aaa, -3);//倒数第三个 const char *ccfffc2= lua_tostring(aaa, -1);//倒数第一个,也就是lua函数的返回值 printf("倒数第三个=%s\n",ccfffc); printf("倒数第一个,也就是lua函数的返回值=%s\n",ccfffc2);
返回结果:
倒数第二个的值为feifei
现在栈内得数目=4
执行函数之后现在栈内得数目=3
倒数第三个=feifei
倒数第一个,也就是lua函数的返回值=第四个结果lua的参数
和预期的一样,lua函数在执行完之后,会吧他自身和他的参数弹出,然后把返回结果压入栈中
2 Lua调用C函数的堆栈变化
首先lua文件中函数为 function testNewCounter2(para) ccc=new222() end c文件中的函数 int t_new(lua_State *aaa){ int ffdsfdsf=lua_gettop(aaa ); printf("函数私有栈的数目=%d\n",ffdsfdsf); lua_pushstring(aaa, "C函数的结果"); int ffdsfdsffds=lua_gettop(aaa); printf("加入一个结果之后的函数私有栈的数目=%d\n",ffdsfdsffds); return 1; }
备注:这里需要说明的一点是返回值为1,说明就一个返回结果,表示压入栈中的返回值数量。因此这个函数无需在压住结果之前清空栈,在他返回后,Lua会自动删除栈中结果之下的内容,就是说不管你加入几个元素进栈,只看返回结果,返回结果是几,就把几个加入到全局栈,其余的会清空,在函数返回完毕之后,函数内的私有栈也全部清空,在最后一步return数目的时候,确实是加入到了全局栈,而函数开始的时候,获取的参数第一个始终是传过来的参数,而不是全局栈中最顶上一个元素,这一点要区分开
调用代码如下:
lua_pushstring(aaa, "feifei"); lua_pushstring(aaa, "wenqian"); lua_pushcfunction(aaa, t_new); lua_setglobal(aaa, "new222"); lua_getglobal(aaa, "testNewCounter2"); lua_pcall(aaa, 0, 0, 0); const char *ccc= lua_tostring(aaa, -1); printf("执行完函数之后倒数第一个=%s\n",ccc); const char *ccc2= lua_tostring(aaa, -2); printf("执行完函数之后倒数第二个=%s\n",ccc);
结果:
函数私有栈的数目=0
加入一个结果之后的函数私有栈的数目=1
执行完函数之后倒数第一个=wenqian
执行完函数之后倒数第二个=feifei
首先来看 t_new函数里面有一个私有栈,不受外部影响,他的栈里面就是传给他的参数。
然后看调用代码,第一和第二是加入两个字符串,然后注册t_new,然后调用
testNewcounter2,调用玩之后,栈弹出这个函数,因为函数没有返回值,所以此时栈内有两个,就是开始加入的两个字符串。
下面再看看让testNewCounter2返回结果时候的调用
这里只是修改lua文件中的方法,改为: function testNewCounter2( ) ccc=new222() return "返回new222的结果" .. ccc end
开始调用 lua_pushstring(aaa, "feifei"); lua_pushstring(aaa, "wenqian"); lua_pushcfunction(aaa, t_new); lua_setglobal(aaa, "new222"); lua_getglobal(aaa, "testNewCounter2"); lua_pcall(aaa, 0, 1, 0); const char *ccc= lua_tostring(aaa, -1); printf("执行完函数之后倒数第一个=%s\n",ccc); const char *ccc2= lua_tostring(aaa, -2); printf("执行完函数之后倒数第二个=%s\n",ccc2);
结果:
函数私有栈的数目=0
加入一个结果之后的函数私有栈的数目=1
执行完函数之后倒数第一个=返回new222的结果C函数的结果
执行完函数之后倒数第二个=wenqian
倒数第一个成了new222返回的结果,和预期的一样
Lua和c的交互中还有一个比较难理解的地方就是upvalue。upvalue实现了一种类似于C语言中静态变量的机制,这种变量只在一个特定的函数中可见。每当在Lua中创建一个函数时,都可以将任意数量的upvalue与这个函数相关联。每个upvalue都可以保存一个lua值。以后,在调用这个函数时,就可以通过伪索引来访问这些upvalue了
closure可以用一个函数代码来创建多个closure,每个closure可以拥有不同的upvalue。
下面得例子,在c语言中创建一个newCounter函数。这个函数是一个工厂函数,每次调用都返回一个新的账户函数
int newCounter(lua_State *aaa){ lua_pushinteger(aaa, 0); lua_pushcclosure(aaa, &counter, 1); int fff=lua_gettop(aaa); printf("推入了closure之后,站内的数量=%d",fff); return 1; } static int counter(lua_State *aaa){ int val=(int)lua_tointeger(aaa, lua_upvalueindex(1)); int fdsfdsf= lua_gettop(aaa); printf("每次进入时栈的数量=%d",fdsfdsf); lua_pushinteger(aaa, ++val); lua_pushvalue(aaa, -1); lua_replace(aaa, lua_upvalueindex(1)); int fdsfdsf11fds1= (int)lua_gettop(aaa); printf("最后栈的数量=%d",fdsfdsf); return 1; } 在Lua文件中,函数如下: function newCounterFactory() counterFun=newCounter(); return counterFun() end function newCounterFactoryAgain() return counterFun() end C语言调用代码如下: lua_pushstring(aaa, "feifei"); lua_pushstring(aaa, "wenqian"); lua_pushcfunction(aaa, newCounter); lua_setglobal(aaa, "newCounter"); lua_getglobal(aaa, "newCounterFactory"); lua_pcall(aaa, 0, 1, 0); const char *ccc= lua_tostring(aaa, -1); printf("第一次调用倒数第一个值=%s\n",ccc); const char *ccc22= lua_tostring(aaa, -2); printf("第一次调用倒数第二个值=%s\n",ccc22); lua_getglobal(aaa, "newCounterFactoryAgain"); lua_pcall(aaa, 0, 1, 0); const char *ccccc= lua_tostring(aaa, -1); printf("第二次调用倒数第一个值=%s\n",ccccc); const char *ccccc2= lua_tostring(aaa, -2); printf("第二次调用倒数第二个值=%s\n",ccccc2); const char *ccccc3= lua_tostring(aaa, -3); printf("第二次调用倒数第三个值=%s\n",ccccc3);
运行结果:
推入了closure之后,站内的数量=1
每次进入时栈的数量=0
最后栈的数量=1
第一次调用倒数第一个值=1
第一次调用倒数第二个值=wenqian
每次进入时栈的数量=0
最后栈的数量=1
第二次调用倒数第一个值=2
第二次调用倒数第二个值=1
第二次调用倒数第三个值=wenqian
从第二次调用可以看出,每一次counter()调用完了之后,确实是加入到了全局栈
现在分析 一下特殊情况
第一:修改一下lua函数,让newCounterFactory只是返回counter函数,而不执行,如下: function newCounterFactory() counterFun=newCounter(); return counterFun end 执行代码如下: lua_pushstring(aaa, "feifei"); lua_pushstring(aaa, "wenqian"); lua_pushcfunction(aaa, newCounter); lua_setglobal(aaa, "newCounter"); lua_getglobal(aaa, "newCounterFactory"); lua_pcall(aaa, 0,1, 0); auto ffdsfdsfdsfdsfff=lua_isfunction(aaa, -1); printf("是不是函数:%d\n",ffdsfdsfdsfdsfff); // lua_pcall(aaa, 0,1, 0); const char *ccc= lua_tostring(aaa, -1); printf("第一次调用倒数第一个值=%s\n",ccc); const char *ccc22= lua_tostring(aaa, -2); printf("第一次调用倒数第二个值=%s\n",ccc22); const char *ccc22222= lua_tostring(aaa, -3); printf("第一次调用倒数第二个值=%s\n",ccc22222);
运行结果:
推入了closure之后,站内的数量=1
是不是函数:1
第一次调用倒数第一个值=(null)
第一次调用倒数第二个值=wenqian
第一次调用倒数第二个值=feifei
分析得知,newCounter没有执行,因为newCounterFactory()只是返回了他,并把它加入了全局栈中,所以全局栈中第一个是
function。
那么我们现在我们知道现在newCounter函数是位于全局栈的顶部,那么我们可以执行一下他,调用代码如下: lua_pushstring(aaa, "feifei"); lua_pushstring(aaa, "wenqian"); lua_pushcfunction(aaa, newCounter); lua_setglobal(aaa, "newCounter"); lua_getglobal(aaa, "newCounterFactory"); lua_pcall(aaa, 0,1, 0); auto ffdsfdsfdsfdsfff=lua_isfunction(aaa, -1); printf("是不是函数:%d\n",ffdsfdsfdsfdsfff); —-执行以下栈顶函数 lua_pcall(aaa, 0,1, 0); const char *ccc= lua_tostring(aaa, -1); printf("第一次调用倒数第一个值=%s\n",ccc); const char *ccc22= lua_tostring(aaa, -2); printf("第一次调用倒数第二个值=%s\n",ccc22); const char *ccc22222= lua_tostring(aaa, -3); printf("第一次调用倒数第二个值=%s\n",ccc22222);
运行结果如下:
推入了closure之后,站内的数量=1
是不是函数:1
每次进入时栈的数量=0
最后栈的数量=1
第一次调用倒数第一个值=1
第一次调用倒数第二个值=wenqian
第一次调用倒数第二个值=feifei
newCounter函数执行完毕,把他的参数和他自己出栈,然后压入他的返回值
那么返过来再看看最初的执行情况,就是newCounterFactory函数里面直接执行newCounter,这里在运行一遍结果 首先改一下lua代码 function newCounterFactory() counterFun=newCounter(); -- return counterFun return counterFun() end 调用代码如下: lua_pushstring(aaa, "feifei"); lua_pushstring(aaa, "wenqian"); lua_pushcfunction(aaa, newCounter); lua_setglobal(aaa, "newCounter"); lua_getglobal(aaa, "newCounterFactory"); lua_pcall(aaa, 0,1, 0); auto ffdsfdsfdsfdsfff=lua_isfunction(aaa, -1); printf("是不是函数:%d\n",ffdsfdsfdsfdsfff); const char *ccc= lua_tostring(aaa, -1); printf("第一次调用倒数第一个值=%s\n",ccc); const char *ccc22= lua_tostring(aaa, -2); printf("第一次调用倒数第二个值=%s\n",ccc22); const char *ccc22222= lua_tostring(aaa, -3); printf("第一次调用倒数第二个值=%s\n",ccc22222);
运行结果:
推入了closure之后,站内的数量=1
每次进入时栈的数量=0
最后栈的数量=1
是不是函数:0
第一次调用倒数第一个值=1
第一次调用倒数第二个值=wenqian
第一次调用倒数第二个值=feifei
和上个例子是一样得,只不过前者是在C里面执行了newCounter,而后者是在lua里面执行的,我认为他们都是一样的,虽然执行完了这个函数就出栈了,
在C语言中无法再次执行了,但是因为在lua中保存了他的一个引用【在哪里引用的?当newCounter执行过程中,lua_pushcclosure讲一个新的
closure留在了栈上,并以此作为newCounter的返回值,而在lua中的全局变量counterFun引用了他的返回值】,所以依然可以再次调用。
下面再看最后一种情况,验证函数里面加入栈的元素,到底对全局栈有什么影响
lua函数如下: function newCounterFactory() counterFun=newCounter(); return counterFun --return counterFun() end C中newCounter方法 int newCounter(lua_State *aaa){ lua_pushstring(aaa, "001"); lua_pushstring(aaa, "002"); lua_pushinteger(aaa, 0); lua_pushcclosure(aaa, &counter, 1); int fff=lua_gettop(aaa); printf("推入了closure之后,站内的数量=%d\n",fff); return 1; } 在这里,我们多推入了几个元素,但是还是返回一个
调用代码如下: lua_pushstring(aaa, "feifei"); lua_pushstring(aaa, "wenqian"); lua_pushcfunction(aaa, newCounter); lua_setglobal(aaa, "newCounter"); lua_getglobal(aaa, "newCounterFactory"); lua_pcall(aaa, 0,1, 0); auto ffdsfdsfdsfdsfff=lua_isfunction(aaa, -1); printf("是不是函数:%d\n",ffdsfdsfdsfdsfff); const char *ccc= lua_tostring(aaa, -1); printf("第一次调用倒数第一个值=%s\n",ccc); const char *ccc22= lua_tostring(aaa, -2); printf("第一次调用倒数第二个值=%s\n",ccc22); const char *ccc22222= lua_tostring(aaa, -3); printf("第一次调用倒数第二个值=%s\n",ccc22222);
运行结果:
推入了closure之后,站内的数量=3
是不是函数:1
第一次调用倒数第一个值=(null)
第一次调用倒数第二个值=wenqian
第一次调用倒数第二个值=feifei
可以看到函数内入栈数目取决于返回值,返回值是几,就入栈几个,加到之前全局栈里面元素的上面,函数内其余的元素都会清除,而函数本身的私有栈也是全部清空了,
通过每次运行函数,在里面调用lua_gettop可以看得出来,私有栈不受全局栈影响,他的第一个参数永远是传入方法的第一个参数,而不是全局栈顶部的元素