lua源码阅读(4)---表

来源:互联网 发布:通信工程预算软件 编辑:程序博客网 时间:2024/05/17 01:49

lua中只有一种数据结构,表,可以可以存储任意类型,只要键不是nil。在Lobject.h中,有表的定义:

typedef struct Table {  CommonHeader;  //表示存在哪些元方法  lu_byte flags;  /* 1<<p means tagmethod(p) is not present */  //  log2( node数组大小)  lu_byte lsizenode;  /* log2 of size of `node' array *///元表  struct Table *metatable;  //数组部分  TValue *array;  /* array part */// Node数组  Node *node;    Node *lastfree;  /* any free position is before this position */  GCObject *gclist;//array大小  int sizearray;  /* size of `array' array */} Table;
lua中的表包含了两部分,一个是数组部分,存储在array指向的数组中,另一个是hash表部分,存储在node指向的数组中。其中Node类型如下:

typedef union TKey {  struct {    TValuefields;   //Value value; int tt    struct Node *next;  /* for chaining */  } nk;    TValue tvk;} TKey;typedef struct Node {  TValue i_val;  TKey i_key;} Node;
Node包含了key和value。

如在lua中 a={3,2,4,x=10},则3,2,4存储在数组部分,x=10存储在hash表部分

在ltable.c中,定义了一些对表的操作的函数。

luaH_set,;uaH_setnum.luaH_strstr是进行新增元素的操作。首先进行查找,如果表中没有改元素,则进行添加的操作,最终调用了newkey函数

static TValue *newkey (lua_State *L, Table *t, const TValue *key) {//计算hash值,得到key所在的hash表中的位置  Node *mp = mainposition(t, key);//如果该位置已经存储了元素  if (!ttisnil(gval(mp)) || mp == dummynode) {      Node *othern;//获取一个空闲的位置Node *n = getfreepos(t);  /* get a free place */    if (n == NULL) {  /* cannot find a free place? *///扩展      rehash(L, t, key);  /* grow table */      return luaH_set(L, t, key);  /* re-insert key into grown table */    }    lua_assert(n != dummynode);//#define key2tval(n)(&(n)->i_key.tvk)othern = mainposition(t, key2tval(mp));if (othern != mp) {  /* is colliding node out of its main position? */      /* yes; move colliding node into free position */        while (gnext(othern) != mp) othern = gnext(othern);  /* find previous */      gnext(othern) = n;  /* redo the chain with `n' in place of `mp' */      *n = *mp;  /* copy colliding node into free pos. (mp->next also goes) */      gnext(mp) = NULL;  /* now `mp' is free */      setnilvalue(gval(mp));    }    else {  /* colliding node is in its own main position */      /* new node will go into free position */      gnext(n) = gnext(mp);  /* chain new position */      gnext(mp) = n;      mp = n;    }  }  gkey(mp)->value = key->value; gkey(mp)->tt = key->tt;  luaC_barriert(L, t, key);  lua_assert(ttisnil(gval(mp)));  return gval(mp);}

首先根据key 计算位置,如果该位置没有存储,则直接存储,否则,发生了hash冲突,需要找到新的空闲位置,存储,加入到链表中。该链表比较特殊,是通过数组实现的。

结构如图


在hash部分的数组中,每一个位置都有一个指针,用于形成链表。这样,hash值相同的元素存储在数组中后,又形成了一条链表。整个hash部分会形成多条链表。这种用数组实现链表的方式与普通的链地址法有些差异。

理解了table的存储结构,其余的操作也就比较好理解了。