2014-09-03 129 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將在任何情況下都能正常工作並保持您的堆棧平衡。