# 版本 lua-5.1.5
lua获取table长度的接口有很多:
推荐一篇文章: 浅析Lua中table的遍历
但是也会出现神奇的情况,请看下面一小段代码:
local t = {111, x = 222, nil, 333, [10] = 555, {}, nil, nil} print(table.getn(t)) -- 4 print(#t) -- 4 print(table.maxn(t)) -- 10 --local t = {111, nil, x = 222, nil, 333, [10] = 555, {}, nil, nil} --local t = {111, x = 222, nil, 333, [10] = 555, nil, {}, nil, nil}
如果随机在t中插入nil,结果会让你大吃一惊,丈二和尚摸不着头脑。比如:
这是为什么呢?有没有规律呢?
这里原因我先简单介绍一下,想弄更清楚的请自己看完之后看源代码,算是抛砖引玉^_^!上面的结果千奇百怪主要是和#操作符有关,#操作符最终获取table长度的函数是(文件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); }
只看很小的一部分:
while (j - i > 1) { unsigned int m = (i+j)/2; if (ttisnil(&t->array[m - 1])) j = m; else i = m; } return i;
unbound_search函数中也有一部分逻辑和上面代码一样
知道二分查找的人花一点时间应该很快能够理解这段代码的意思,从数组[i-j]中二分查找的方式遍历,如果不为ni的话,相当于长度增加了(i=m);如果为nil的话,相当于长度减少了,也举个简单的例子:
local t = {1, 2, nil} print("#t1 = ", #t1) --[[ i=0, j=3 (1)m=1, array[m-1]=1不为nil, i=m=1 (2)m=2, array[m-1]=2不为nil, i=m=2 (3)j-i <=1循环结束,return i,结果为2 ]] local t = {1, nil, 2, nil} print("#t1 = ", #t1) --[[ i=0, j=4 (1)m=2, array[m-1]=nil, j=m=2 (2)m=1, array[m-1]=1不为nil, i=m=1 (3)j-i <=1循环结束,return i,结果为1 ]]
可以看到,ni的位置不确定性,让返回的长度变幻莫测了,这也就是为什么上面看到的结果千奇百怪了 ^_^
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).’ ## unpack({…}) 我们会经常用到这种方式,比如传进来的参数是不定长的’…‘,然后用table的操作符’{}’括起来,变成一个table,然后使用unpack解包。恰好,前两天有个同事跟我们分享了这个使用方式的漏洞。一开始有点吃惊,看完上面的一些分析只会,也就能够明晰许多了。下面是同事的分享。 ### 问题代码
function fnUnPack( ... ) local p = {...}; return unpack(p); end print(fnUnPack(1, 2, 3)); print(fnUnPack(1, 2, 3, nil ,4)); print(fnUnPack(1, 2, 3, nil ,4, nil)); --结果 1 2 3 1 2 3 nil 4 1 2 3
示例代码:
function fnUnPack1( ... ) local p = table.pack(...); return table.unpack(p, 1, p.n); end
版本lua-5.2及以上
示例代码:
function fnUnPack2( ... ) local p = {...} local n = select('#', ...) return unpack(p, 1, n); end
##版本5.2及以上