2014-09-03 194 views
6

我正在开发一个使用Lua进行脚本编写的程序,有时会崩溃。有了GDB,我认为我发现了这个问题,但我不知道它是否解决了问题,因为段错误只会偶尔发生。因此,旧的代码是这样的:Lua:这是否会导致段错误

void Call(std::string func){ 
    lua_getglobal(L, func.c_str()); //This is the line GDB mentioned in a backtrace 
    if(lua_isfunction(L,lua_gettop(L))) { 
     int err = lua_pcall(L, 0, 0,0); 
     if(err != 0){ 
      std::cout << "Lua error: " << luaL_checkstring(L, -1) << std::endl; 
     } 
    } 
} 

的事情是,该功能将被称为每秒几次,但它需要调用的函数并不总是确定的,所以我认为堆栈会溢出。我添加了以下行:

lua_pop(L,lua_gettop(L)); 

段错误没有发生了。这可能是问题吗?

回答

5

使用lua_gettop(L)作为参数lua_pop将清除整个Lua API堆栈(相当于lua_settop(0)),这可能不是您想要的。但确实你的问题是,lua_getglobal总是推动一些东西;如果不存在具有给定名称的全局,则它将推动nil,就像等效的Lua表达式一样。但是,lua_pcall弹出函数和所有参数(如果有的话;在你的情况下你指定为零),所以如果函数存在,你不会遇到问题。你应该做的是将lua_pop(L, 1)添加到外部ifelse子句。这样,你的功能将永远是平衡(即保持原样)。

您可以在Lua manual中看到这些内容:对于每个函数,它都在说明中进行了说明,并且在函数原型旁边的括号内以灰色表示。例如,lua_getglobal具有[-0,+1,e],表示它不会弹出任何元素,并且(总是)推入一个元素(并且意味着它可能会导致错误)。

+1

应该指出的是,在Lua-> C过渡期间,lua确保你有一个新的堆栈,并且在那个C-> lua过渡lua确保堆栈被清理掉了,在这些方面的平衡是必需的(尽管如此,这仍然是一个很好的做法)。但是,只要你留在C中,就需要栈管理(和功能栈平衡)。 – 2014-09-03 14:40:56

1

根据www.lua.org/manual/5.2/manual.html#4.2,你有责任保持栈清洁,因为Lua不会执行任何检查。我想假设 segfault是不保持堆栈清理(新值推到实际堆栈空间之外的存储器位置,并且也在您的进程虚拟内存之外)的合理结果。

由于您不会调用lua_pcall从堆栈中删除非函数,因此会无限期地增加堆栈。

你的解决方案应该工作,除非被调用的函数在堆栈上留下了一些东西。 由于您将结果设置为0,Lua不会在堆栈上留下任何结果。

我对lua_pcall参考的理解是,即使nresults是0,错误代码仍然可以留在堆栈上。编辑:我也检查了这种行为,它完全如我所想的那样。

在这种情况下,在开始时调用lua_gettop并在最后调用lua_settop将在任何情况下都能正常工作并保持您的堆栈平衡。