我们首先关心的是如何在Lua中表示数组的值。Lua为这种情况提供专门提供一个基本的类型:userdata。一个userdatum提供了一个在Lua中没有预定义操作的raw内存区域。

Lua API提供了下面的函数用来创建一个userdatum:

void *lua_newuserdata (lua_State *L, size_t size);

lua_newuserdata函数按照指定的大小分配一块内存,将对应的userdatum放到栈内,并返回内存块的地址。如果出于某些原因你需要通过其他的方法分配内存的话,很容易创建一个指针大小的userdatum,然后将指向实际内存块的指针保存到userdatum里。我们将在下一章看到这种技术的例子。

使用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)调用,后面我们将看到如何使用metatables来支持常规的写法array[index] = value。对于这两种写法,下面的函数是一样的,数组下标从1开始:

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函数,他根据给定的名字创建一个表,并使用arraylib数组中的name-function对填充这个表。

打开上面定义的库之后,我们就可以在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元素的数组大概占用800KB的内存,同样的条件由Lua 表实现的数组需要1.5MB的内存。

Userdata的更多相关文章

  1. Lua 之 userdata

    Lua 之 userdata 在Lua中可以通过自定义类型(user data)与C语言代码更高效.更灵活的交互,从而扩展Lua能够表达的类型. full userdata full userdata ...

  2. 快速编译system.img、userdata.img、boot.img的方法

    快速编译system.img和boot.img的方法 快速编译system.img,可以使用这个命令: #make systemimage 快速编译boot.img,可以使用以下命令: #make b ...

  3. zImage.img、ramdisk.img、system.img、userdata.img介绍及解包、打包方法

    ramdisk.img system.img userdata.img介绍及解包.打包方法 Android 源码编译后,在out/target/product/generic下生成ramdisk.im ...

  4. Android 的 ramdisk.img、system.img、userdata.img 作用说明,以及UBoot 系统启动过程

    首先通過編譯,先將android內核編譯成功.正常情況下,在目錄out/target.product/generic/(但是有的就沒有generic文件,如freescale和iriver:但是lon ...

  5. Qt之界面数据存储与获取(使用setUserData()和userData())

    在GUI开发中,往往需要在界面中存储一些有用的数据,这些数据可以来配置文件.注册表.数据库.或者是server. 无论来自哪里,这些数据对于用户来说都是至关重要的,它们在交互过程中大部分都会被用到,例 ...

  6. localstorage || globalStorage || userData

    globalStorage 这个也是html5中提出来,在浏览器关闭以后,使用globalStorage存储的信息仍能够保留下来,并且存储容量比IE的userdata大得多,一个域下面是5120k.和 ...

  7. js本地存储解决方案(localStorage与userData)

    WEB应用的快速发展,是的本地存储一些数据也成为一种重要的需求,实现的方案也有很多,最普通的就是cookie了,大家也经常都用,但是cookie的缺点是显而易见的,其他的方案比如:IE6以上的user ...

  8. 基于'sessionStorage'与'userData'的类session存储

    Storage.js: 注意:此版本实现的存储在符合Web存储标准(ie8及ie8以上的版本与其他主流浏览器)的情况下与session的周期一致,但在页面不关闭的情况下没有过期时间,ie7及以下版本则 ...

  9. web本地存储-UserData

    userData,IE中持久化用户数据的方法. 使用userData用户数据首先必须使用css指定userData行为.代码示例: var ud = document.createElement(&q ...

  10. Lua中的userdata

    [话从这里说起] 在我发表<Lua中的类型与值>这篇文章时,就有读者给我留言了,说:你应该好好总结一下Lua中的function和userdata类型.现在是时候总结了.对于functio ...

随机推荐

  1. 如何在cmd命令行中查看、修改、删除与添加环境变量,语法格式例子:set path;echo %APPDATA%

    如何在cmd命令行中查看.修改.删除与添加环境变量 首先明确一点: 所有的在cmd命令行下对环境变量的修改只对当前窗口有效,不是永久性的修改.也就是说当关闭此cmd命令行窗口后,将不再起作用.永久性修 ...

  2. 网站跳转到cgi-sys/defaultwebpage.cgi的原因和解决方式

    cpanel遇到这种问题,看了这篇文章老鹰主机域名解析A记录教程–关于cgi-sys/defaultwebpage.cgi后,尝试后     首先ping 域名,结果如下     看到没有ping结果 ...

  3. Linux内核(13) - 子系统的初始化之以PCI子系统为例

    由Kconfig这张地图的分布来看,PCI这块儿的代码应该分布在两个地方,drivers/pci和arch/i386/pci,两岸三地都属于一个中国,不管是drivers/pci那儿的,还是arch/ ...

  4. Java+Oracle实现事务——JDBC事务

    J2EE支持JDBC事务.JTA事务和容器事务事务,这里说一下怎样实现JDBC事务. JDBC事务是由Connection对象所控制的,它提供了两种事务模式:自己主动提交和手动提交,默认是自己主动提交 ...

  5. Python 列表 sort() 方法

    描述 Python 列表 sort() 方法对列表进行排序. 语法 sort() 方法语法: L.sort([key=None][,reverse=False]) 参数 key-- 可选参数, 如果指 ...

  6. openssl之EVP系列之9---EVP_Digest系列函数的一个样例

    openssl之EVP系列之9---EVP_Digest系列函数的一个样例     ---依据openssl doc/crypto/EVP_DigestInit.pod翻译     (作者:Drago ...

  7. echarts geo地图坐标转换为页面Offset坐标

    https://github.com/apache/incubator-echarts/issues/2540 代码示例: // 获取系列 ) // 获取地理坐标系实例 var coordSys = ...

  8. mysql-5.7 innodb change buffer 详解

    一.innodb change buffer 介绍: 1.innodb change buffer 是针对oltp场景下磁盘IO的一种优化(我也感觉这个不太像人话,但是它又非常的准确的说明 innod ...

  9. Spring里的aop实现方式和源码分析

    使用"横切"技术,AOP把软件系统分为两个部分:核心关注点和横切关注点.业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点.横切关注点的一个特点是,他们经常发生在核心关 ...

  10. Android Studio 2.2 Record Espresso Test

    Android Studio 已经更新到了2.2,在 Run 中发现了 Record Espresso Test 功能,很强大,但是不稳定. 尝试了下在 Android 6.0 上的登录页面,可以通过 ...