首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C >> C 語言入門

C語言調用Lua函數完整指南:棧交互與錯誤處理

     編輯:C 語言入門

Lua C API 調用流程

調用 Lua 函數僅需四步,核心是棧操作:

  1. lua_getglobal(L, "f"):將函數壓入棧
  2. lua_pushnumber(L, x):壓入參數(支持多種類型)
  3. lua_pcall(L, nargs, nresults, errfunc):受保護調用
  4. lua_tonumber(L, -1):取結果,lua_pop(L, 1) 清理棧

完整封裝示例

/*** Lua 端函數 ***
function f(x, y)
    return (x^2 * math.sin(y)) / (1 - x)
end
***/

/* C 端封裝調用 */
double call_lua_f(lua_State *L, double x, double y) {
    double z;
    
    // 1. 獲取全局函數
    lua_getglobal(L, "f");
    
    // 2. 壓入兩個參數
    lua_pushnumber(L, x);
    lua_pushnumber(L, y);
    
    // 3. 受保護調用:2個參數,1個返回值,無錯誤處理函數
    if (lua_pcall(L, 2, 1, 0) != 0) {
        // 錯誤信息在棧頂
        const char *err = lua_tostring(L, -1);
        fprintf(stderr, "Lua error: %s\n", err);
        lua_pop(L, 1);  // 彈出錯誤信息
        return NAN;
    }
    
    // 4. 類型檢查與取值
    if (!lua_isnumber(L, -1)) {
        fprintf(stderr, "Function must return a number\n");
        lua_pop(L, 1);
        return NAN;
    }
    z = lua_tonumber(L, -1);
    lua_pop(L, 1);  // 彈出結果,棧平衡
    return z;
}

關鍵機制深度解析

棧操作原理

  • 調用前棧: [函數, 參數1, 參數2, ...] (頂在右)
  • lua_pcall 執行: 彈出函數和參數,執行,壓入結果
  • 調用後棧: [結果1, 結果2, ...] (多返回值,第一個在底部)
  • 錯誤時: 壓入錯誤信息字符串,返回非零錯誤碼

錯誤處理最佳實踐

// 使用錯誤處理函數(第4參數)
lua_pushcfunction(L, my_error_handler);  // 壓入錯誤處理器
int errfunc_idx = lua_gettop(L);         // 記錄其索引

lua_getglobal(L, "risky_func");
lua_pushnumber(L, arg);
if (lua_pcall(L, 1, 1, errfunc_idx) != 0) {
    // 錯誤已由 handler 處理,棧頂為處理後的錯誤對象
    handle_error(L);
}
lua_remove(L, errfunc_idx);  // 移除錯誤處理器

多返回值處理

lua_pcall(L, 0, 3, 0);  // 期望3個返回值
// 棧頂為第3個返回值,依次向下
double r3 = lua_tonumber(L, -1);
double r2 = lua_tonumber(L, -2);
double r1 = lua_tonumber(L, -3);
lua_pop(L, 3);

常見陷阱與避坑指南

問題原因解決
棧不平衡忘記 pop 結果/錯誤信息每次 push/調用後檢查棧頂,確保對稱 pop
類型錯誤Lua 返回非預期類型用 lua_is* 系列檢查再轉換
錯誤信息丟失pcall 失敗未讀取棧頂錯誤時必讀 lua_tostring(L, -1) 再 pop
全局變量污染直接操作 _G優先用 lua_getglobal/lua_setglobal

進階:註冊 C 函數供 Lua 調用

static int l_sin(lua_State *L) {
    double d = luaL_checknumber(L, 1);
    lua_pushnumber(L, sin(d));
    return 1;  // 返回值個數
}

static const luaL_Reg mylib[] = {
    {"sin", l_sin},
    {NULL, NULL}
};

int luaopen_mylib(lua_State *L) {
    luaL_newlib(L, mylib);
    return 1;
}

編譯為動態庫,Lua 中 require "mylib" 即可使用 mylib.sin(x)。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved