第一版是為 Lua 5.0 編寫的。雖然在很大程度上仍然適用於後續版本,但還是有一些差異。
第四版針對 Lua 5.3,可在 Amazon 和其他書店購買。
購買本書,您也可以幫助 支援 Lua 專案


24.2 – 堆疊

在嘗試在 Lua 和 C 之間交換值時,我們會遇到兩個問題:動態類型系統和靜態類型系統之間的不匹配,以及自動記憶體管理和手動記憶體管理之間的不匹配。

在 Lua 中,當我們撰寫 a[k] = v 時,kv 都可以有數種不同的類型(甚至 a 也可能因為元表而有不同的類型)。然而,如果我們要在 C 中提供此操作,任何 settable 函式都必須有固定的類型。我們需要數十個不同的函式來執行此單一操作(每個函式對應三個引數的每種類型組合)。

我們可以透過在 C 中宣告某種聯合類型(讓我們稱之為 lua_Value)來解決此問題,它可以表示所有 Lua 值。然後,我們可以宣告 settable

    void lua_settable (lua_Value a, lua_Value k, lua_Value v);
此解決方案有兩個缺點。首先,將如此複雜的類型對應到其他語言可能很困難;Lua 的設計不只可以輕鬆與 C/C++ 介接,也可以與 Java、Fortran 等介接。其次,Lua 會進行垃圾回收:如果我們在 C 變數中保留 Lua 值,Lua 引擎無法得知此用途;它可能會(錯誤地)假設此值是垃圾並加以回收。

因此,Lua API 沒有定義任何類似 lua_Value 的類型。相反地,它使用抽象堆疊在 Lua 和 C 之間交換值。此堆疊中的每個插槽都可以容納任何 Lua 值。每當您想要從 Lua 要求值(例如,全域變數的值)時,您會呼叫 Lua,它會將所需的值推入堆疊。每當您想要傳遞值給 Lua 時,您會先將值推入堆疊,然後呼叫 Lua(它會彈出值)。我們仍然需要不同的函式將每個 C 類型推入堆疊,以及不同的函式從堆疊取得每個值,但我們避免了組合爆炸。此外,由於此堆疊是由 Lua 管理,垃圾回收器知道 C 使用哪些值。

API 中幾乎所有函式都使用堆疊。正如我們在第一個範例中所見,luaL_loadbuffer 會將其結果留在堆疊中(已編譯的區塊或錯誤訊息);lua_pcall 會從堆疊取得要呼叫的函式,並將任何偶發的錯誤訊息留在堆疊中。

Lua 以嚴格的 LIFO 規律(後進先出;也就是說,總是透過頂端)來操作此堆疊。當您呼叫 Lua 時,它只會變更堆疊的最上層部分。您的 C 程式碼有更多自由度;特別是,它可以檢查堆疊內的任何元素,甚至可以在任何任意位置插入和刪除元素。