Lua源码阅读笔记 - table的长度

本文详细解析了Lua中table的长度计算原理,包括不同情况下如何确定table长度,以及为何sizearray初始设置为8。文章通过源码分析,解释了在table赋值过程中如何触发rehash以及内存管理策略,特别讨论了数组利用率和动态内存分配的问题。
摘要由CSDN通过智能技术生成

table数据结构

首先看一下lua中table的数据结构:

// lobject.h

/*
** Tables
*/

typedef union TKey {
   
  struct {
   
    TValuefields;
    struct Node *next;  /* for chaining */
  } nk;
  TValue tvk;
} TKey;


typedef struct Node {
   
  TValue i_val;
  TKey i_key;
} Node;


typedef struct Table {
   
  CommonHeader;
  lu_byte flags;  /* 1<<p means tagmethod(p) is not present */ 
  lu_byte lsizenode;  /* log2 of size of `node' array */
  struct Table *metatable;
  TValue *array;  /* array part */
  Node *node;
  Node *lastfree;  /* any free position is before this position */
  GCObject *gclist;
  int sizearray;  /* size of `array' array */
} Table;

table由两部分组成:

  1. array是数组头,sizearray是数组长度。
  2. node是哈希表头,lsizenode是以2为底哈希表长度的对数,即 2lsizenode 为哈希表长度。

table的长度

执行如下lua代码:

tb1 = {1, 2, 3, 4, 5}
tb1[7] = 7
print("tb1 length: "..#tb1)
-- tb1 length: 5

tb2 = {1, 2, 3, 4, 5}
tb2[8] = 8
print("tb2 length: "..#tb2)
-- tb2 length: 8

tb3 = {1, 2, 3, 4, 5}
tb3[8] = 8
tb3[9] = 9
print("tb3 length: "..#tb3)
-- tb3 length: 9

tb4 = {1, 2, 3, 4, 5}
tb4[2] = nil
tb4[3] = nil
print("tb4 length: "..#tb4)
-- tb4 length: 5

tb1长度5,tb2长度8,tb3长度9,tb4长度5。
为什么会这样呢?可以找一下lua中#获取table长度的源代码。
在 lvm.c 文件中的函数void luaV_execute (lua_State *L, int nexeccalls)内,我们可以通过调试找到#对应的操作码OP_LEN,发现#获取table长度即是调用了int luaH_getn (Table *t)

// ltable.c

/*
** Try to find a boundary in table `t'. A `boundary' is an integer index
** such that t[i] is non-nil and t[i+1] is nil (and 0 if t[1] is nil).
*/
int luaH_getn (Table *t) {
   
  unsigned int j = t->sizearray;
  if (j > 0 && ttisnil(&t->array[j - 1])) {
   
    /* there is a boundary in the array part: (binary) search for it */
    unsigned int i = 0;
    while (j - i > 1) {
   
      unsigned int m = (i+j)/2;
      if (ttisnil(&t->array[m - 1])) j = m;
      else i = m;
    }
    return i;
  }
  /* else must find a boundary in hash part */
  else if (t->node == dummynode)  /* hash part is empty? */
    return j;  /* that is easy... */
  else return unbound_search(t, j);
}

函数中分三种情况:

  1. table长度大于0,且最后一个元素是nil
  2. table的哈希表为空
  3. 其他情况
  • 对于tb1sizearray为8,则数组部分是{1, 2, 3, 4, 5, nil, 7, nil}
    符合情况1,则二分查找Number和nil的分界,所以最终返回值是5。
  • 对于tb2sizearray也是8,数组部分是{1, 2, 3, 4, 5, nil, nil, 8},最后一个元素不为nil,哈希表部分为空。
    符合情况2,所以直接返回sizearray即8。
  • 对于tb3sizearray为8,索引9在哈希表部分,数组部分与tb2相同。
    符合情况3,调用int unbound_search (Table *t, unsigned int j)
// ltable.c

static int unbound_search (Table *t, unsigned int j) {
   
  unsigned int i = j;  /* i is zero or a present index */
  j++;
  /* find `i' and `j' such that i is present and j is not */
  while (!ttisnil(luaH_getnum(t, j))) {
   
    i = j;
    j *= 2;
    if (j > cast(unsigned int, MAX_INT)) {
     /* overflow? */
      /* table was built with bad purposes: resort to linear search */
      i = 1;
      while (!ttisnil(luaH_getnum(t, i))) i++;
      return i - 1;
    }
  }
  /* now do a binary search between them */
  while (j - i > 1) {
   
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值