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可以看得出来,私有栈不受全局栈影响,他的第一个参数永远是传入方法的第一个参数,而不是全局栈顶部的元素