lua源码学习笔记

  • 1. lua
  • 1) 基本数据类型
  • 2)lua是动态语言,提供了虚拟机;最终代码是以字节码的形式由解释器执行
  • 3)闭包
  • 4) lua和C的相互调用
  • 5) 协程
  • 6) 元表
  • 2. lua数据类型分析
  • 1) string
  • 2) userdata
  • 3) table
  • 4) 总结


1. lua
1) 基本数据类型

nil,number,string,userdata,function,thread,boolean,table

2)lua是动态语言,提供了虚拟机;最终代码是以字节码的形式由解释器执行
3)闭包
  1. 通过调用含有一个内部函数加上该外部函数持有的外部局部变量的外部函数产生的一个函数示例:<函数内部定义的新函数可以访问外部函数的变量 >
  2. 组成:外部函数+外部函数创建的值+内部函数(闭包)
// 排序
table.sort(t, function(a, b) return a < b end);

// 内部自增
function add()
    local index = 1
    return function() ---假设这个匿名函数为a
        index = index  + 1
        return index 
    end
end
4) lua和C的相互调用
  1. lua和C交互是通过lua的堆栈实现
  2. Lua的堆栈是在创建Lua_State时创建
  3. 调用形式:
  1. c++调用lua:c++将数据入栈,lua读取数据再将结果入栈
  2. lua调用c++:编写自己的C模块,注入到lua解释器中,由lua调用该模块
  1. 流程
//1. 创建lua状态
LuaL_newState()
//2. 加载Lua文件
LuaL_loadfile()
//3. 运行Lua文件
Lua_pcall()
//4. 数据读取
Lua_getglobal()
//5. 关闭stat
lua_close()
5) 协程
  1. 与线程类似,拥有独立的堆栈,独立的局部变量;同时又能与其他协程共享其他全局变量
  2. 和线程区别:线程由操作系统调度;协程由代码调度可控
  3. 协程的三个状态:
  1. suspended:挂起
  2. running:运行
  3. dead:函数执行完
  1. 执行函数:
//1. 创建一个协程
coroutine.create()
//2. 从挂起到运行
coroutine.resume()
//3. 挂起正在运行的协程
coroutine.yield()
6) 元表
  1. 在lua的table我们可以访问对应的key得到value的值,但是却无法对两个table进行操作;因此lua提供了元表,允许我们改变table的行为,每个行为关联了对应的元方法
  2. 有好几种内置的元方法(__add, __call, __tostring,__index。。。)
  3. 元方法的功能很强大,使用元方法可以实现很多功能
2. lua数据类型分析
1) string

主要存储数据

  1. len:字符串的长度:lua中没有’\0’,需要len记录其长度
  2. hash:记录存储的桶的位置
  3. extra:用于标识是长字符串还是短字符串
  1. lua中字符串在内部有两种形式:长字符串和短字符串,以LUAI_MAXSHORTLEN宏为界限,默认40字节
  2. 字符串一旦被创建,则不能被改写。lua的值对象若为字符串类型,则以引用的方式存在
  3. 字符串没有被任何引用的地方就会被回收
  4. 在使用过程中使用…链接字符串并不会改变原串的内容,而是会开辟一块新的内存空间存放
  5. 字符串比较方法
  1. 如果是短字符串直接比较地址;否则调用luaS_eqlngstr进行比较
  2. 比较长字符串时长度相同挨个字符比较,否则返回长的
  1. lua字符串对象的构造
2) userdata
  1. userdata分为两种:轻量级的和完全类型的
  1. 轻量级的:是一种表示c指针的值,对于lua虚拟机来说,这种类型不需要GC,其指向的内存由用户分配和释放
  2. 类型完全的:内存由lua虚拟机分配,并由GC负责处理
  1. 在存储形式上和字符串相同;但是有元表,不被内部化处理,也不需要’\0’;没有单独分配内存
  2. 用途
  1. 表示在C/C++中的类型,实现lua拓展
  2. userdata元表通常用来存储对于C/C++类型的方法
3) table
  1. 初始化
  1. lua的table被分为两个部分:数组部分和哈希部分
  2. hash部分分配的大小永远使2的整数次幂
  1. 插入
  1. 插入操作被实现为查询已有键值,若不存在就创建新键,然后在插入
  2. hash部分以hash表的方式存放。每个可能的键值在hash表中都有一个主要位置,mainposition。创建一个新键时,检查mainposition如果没有使用 ,直接使用这个新键;若已被占用,使用单链表的形式链接起来
  1. 遍历
  1. 当查询的键位整数键且在数组范围内时,在数组部分查询
  2. 否则根据键的hash值取hash表查找,拥有相同键的hash冲突时,在hash表中由node的next域链接,遍历链表即可
  1. 序列长度
  1. lua中取长度只有table使一个序列使才有定义
  2. 取长度有三种情况:
  1. 数组部分刚好被占满,没有hash部分,直接返回sizearray
  2. 数组部分没有被占满,通过二分查找检索到数组最后一个非空位置,返回序列长度
  3. 数组部分被占满,且有hash部分,先计算数组部分的长度,再在hash部分搜索整数键值
  1. 迭代器
  1. ipairs:简单的把每次传入的迭代值加1再尝试从table中索引,如果没有返回nil并停止索引
  2. pairs
4) 总结
  1. lua对于table中值为nil的键空间不会回收,而是在与预先准备好的hash空间使用完后惰性回收,通过rehash
  2. 构造table时尽量使用构造语句并与预先分配空间
  3. 避免整数键和其他键的混合使用,数组部分和哈希部分有空间不足的情况时会触发rehash过程