第一版是为 Lua 5.0 编写的。虽然在很大程度上仍然适用于后续版本,但还是有一些差异。
第四版针对 Lua 5.3,可在 亚马逊 和其他书店购买。
购买本书,您还可以帮助支持 Lua 项目


28.1 – 用户数据

我们首先关注如何在 Lua 中表示数组值。Lua 提供了一种专门用于此的基本类型:用户数据。用户数据在 Lua 中提供了一个原始内存区域,没有预定义的操作。

Lua API 提供了以下函数来创建用户数据

    void *lua_newuserdata (lua_State *L, size_t size);
lua_newuserdata 函数分配一个具有给定大小的内存块,将相应用户数据压入堆栈,并返回块地址。如果您出于某种原因需要通过其他方式分配内存,那么创建一个大小为指针的用户数据并存储指向真实内存块的指针非常容易。我们将在下一章中看到此技术的示例。

使用 lua_newuserdata,创建新数组的函数如下

    static int newarray (lua_State *L) {
      int n = luaL_checkint(L, 1);
      size_t nbytes = sizeof(NumArray) + (n - 1)*sizeof(double);
      NumArray *a = (NumArray *)lua_newuserdata(L, nbytes);
      a->size = n;
      return 1;  /* new userdatum is already on the stack */
    }
luaL_checkint 函数是 luaL_checknumber 的整数变体。)一旦 newarray 在 Lua 中注册,您就可以使用类似 a = array.new(1000) 的语句创建新数组。

要存储一个条目,我们将使用类似 array.set(array, index, value) 的调用。稍后我们将看到如何使用元表来支持更传统的语法 array[index] = value。对于这两种表示法,底层函数都是相同的。它假定索引从 1 开始,这在 Lua 中很常见。

    static int setarray (lua_State *L) {
      NumArray *a = (NumArray *)lua_touserdata(L, 1);
      int index = luaL_checkint(L, 2);
      double value = luaL_checknumber(L, 3);
    
      luaL_argcheck(L, a != NULL, 1, "`array' expected");
    
      luaL_argcheck(L, 1 <= index && index <= a->size, 2,
                       "index out of range");
    
      a->values[index-1] = value;
      return 0;
    }
luaL_argcheck 函数检查给定的条件,并在必要时引发错误。因此,如果我们使用错误的参数调用 setarray,我们将获得一个有启发性的错误消息
    array.set(a, 11, 0)
    --> stdin:1: bad argument #1 to `set' (`array' expected)

下一个函数检索一个条目

    static int getarray (lua_State *L) {
      NumArray *a = (NumArray *)lua_touserdata(L, 1);
      int index = luaL_checkint(L, 2);
    
      luaL_argcheck(L, a != NULL, 1, "`array' expected");
    
      luaL_argcheck(L, 1 <= index && index <= a->size, 2,
                       "index out of range");
    
      lua_pushnumber(L, a->values[index-1]);
      return 1;
    }
我们定义另一个函数来检索数组的大小
    static int getsize (lua_State *L) {
      NumArray *a = (NumArray *)lua_touserdata(L, 1);
      luaL_argcheck(L, a != NULL, 1, "`array' expected");
      lua_pushnumber(L, a->size);
      return 1;
    }
最后,我们需要一些额外的代码来初始化我们的库
    static const struct luaL_reg arraylib [] = {
      {"new", newarray},
      {"set", setarray},
      {"get", getarray},
      {"size", getsize},
      {NULL, NULL}
    };
    
    int luaopen_array (lua_State *L) {
      luaL_openlib(L, "array", arraylib, 0);
      return 1;
    }
我们再次使用辅助库中的 luaL_openlib。它创建一个具有给定名称的表(在我们的示例中为 "array"),并使用 arraylib 数组指定的名称-函数对填充它。

打开库后,我们就可以在 Lua 中使用我们的新类型了

    a = array.new(1000)
    print(a)               --> userdata: 0x8064d48
    print(array.size(a))   --> 1000
    for i=1,1000 do
      array.set(a, i, 1/i)
    end
    print(array.get(a, 10))  --> 0.1

在 Pentium/Linux 上运行此实现,一个具有 100K 个元素的数组占用 800 KB 内存,正如预期的那样;一个等效的 Lua 表需要超过 1.5 MB。