0基础lua学习(十八)C调用Lua----02Lua堆栈

1. Lua与C通信,为什么使用虚拟的一个堆栈?

     当在 Lua 和 C 之间交换数据有两个问题:

  • 动态与静态类型系统的不匹配
  • 自动与手动内存管理的不一致

Lua中a[k]=vak可能的类型,有很多种,我们要想映射这个变量可能要写三个参数类型的每一种组合函数。(表、数字、字符串)

Cunion 类型能来解决这个问题吗?

如此复杂的类型映射到其它语言可能很困难,

  •  Lua 不仅被设计为与 C/C++易于交互, Java,Fortran 以及类似的语言也一样。
  •  Lua负责垃圾回收:如果我们将 Lua 值保存在 C 变量中, Lua 引擎没有办法了解这种用法;它可能错误地认为某个值为垃圾并收集他。

 所以,不能使用union关键字

替代的方案:

它用一个抽象的栈在 Lua 与 C 之间交换值。栈中的每一条记录都可以保存任何 Lua 值。

 


2.获取参数的过程:

无论你何时想要从 Lua 请求一个值(比如一个全局变量的值),调用 Lua,被请求的值将会被压入栈。无论你何时想要传递一个值给 Lua,首先将这个值压入栈,然后调用 Lua(这个值将被弹出)。 我们仍然需要一个不同的函数将每种 C 类型压入栈和一个不同函数从栈上取值(注:只是取出不是弹出),但是我们避免了多种不同类型参数的函数组合。

另外,因为栈是由 Lua 来管理的,垃圾回收器知道那个值正在被 C 使用。 几乎所有的 API函数都用到了栈。

 


3.Lua栈的规则:

Lua 以一个严格的 LIFO 规则(后进先出;也就是说,始终存取栈顶)来操作栈。
当你调用 Lua 时,它只会改变栈顶部分。你的C代码却有更多的自由;更明确的来讲,你可以查询栈上的任何元素,甚至是在任何一个位置插入和删除元素

 


4.压入元素

void lua_pushnil (lua_State *L);
void lua_pushboolean (lua_State *L, int bool);
void lua_pushnumber (lua_State *L, double n);
void lua_pushlstring (lua_State *L, const char *s,size_t length);

//任意的字符串(char*类型,允许包含'\0'字符)用 lua_pushlstring,

//C语言风格(以'\0'结束)的字符串( const char*)用 lua_pushstring:
void lua_pushstring (lua_State *L, const char *s);

Lua 中的字符串不是以零为结束符的;它们依赖于一个明确的长度,因此可以包含

任意的二进制数据。将字符串压入串的正式函数是lua_pushlstring,它要求一个明确的长度作为参数。

  • 对于以零结束的字符串,你可以用lua_pushstring(它用 strlen 来计算字符串长度)。
  • Lua 从来不保持一个指向外部字符串(或任何其它对象,除了 C 函数——它总
    是静态指针)的指针。
  • 对于它保持的所有字符串,Lua 要么做一份内部的拷贝要么重新利用已经存在的字符串。因此,一旦这些函数返回之后你可以自由的修改或是释放你的缓冲区。

5.查询元素

  • API 用索引来访问栈中的元素。在栈中的第一个元素(也就是第一个被压入栈的)
    有索引 1,下一个有索引2,以此类推。
  • 我们也可以用栈顶作为参照来存取元素,利用负索引。在这种情况下,-1 指出栈顶元素(也就是最后被压入的),-2 指出它的前一个元素,以此类推。
LUA_API int             (lua_isnumber)(lua_State *L,int idx);

LUA_API int             (lua_isstring)(lua_State *L,int idx);

LUA_API int             (lua_iscfunction)(lua_State *L,int idx);

LUA_API int             (lua_isuserdata)(lua_State *L,int idx);

int lua_is... (lua_State *L, int index);

//获取值

int lua_toboolean (lua_State *L, int index);
double lua_tonumber (lua_State *L, int index);
const char * lua_tostring (lua_State *L, int index);
size_t lua_strlen (lua_State *L, int index);


 

Lua_tostring 函数返回一个指向字符串的内部拷贝的指针。你不能修改它(使你想起
那里有一个 const)。只要这个指针对应的值还在栈内, Lua 会保证这个指针一直有效。

当一个 C 函数返回后, Lua 会清理他的栈,所以,有一个原则:永远不要将指向 Lua 字符串的指针保存到访问他们的外部函数中。

Lua_string 返回的字符串结尾总会有一个字符结束标志 0, 但是字符串中间也可能包
0lua_strlen 返回字符串的实际长度。特殊情况下,假定栈顶的值是一个字符串,下
面的断言(assert)总是有效的:

const char *s = lua_tostring(L, -1); /* any Lua string */
size_t l = lua_strlen(L, -1); /*its length */
assert(s[l] == '\0');
assert(strlen(s) <= l);


 


6.其他堆栈操作

除开上面所提及的 C 与堆栈交换值的函数外, API 也提供了下列函数来完成通常的堆栈维护工作:

//函数 lua_gettop 返回堆栈中的元素个数,它也是栈顶元素的索引。注意一个负数//索引-x 对应于正数索引 gettop-x+1。
int lua_gettop(lua_State *L);

//lua_settop设置栈顶(也就是堆栈中的元素个数)为一个指定的值。如果开始的栈顶高于新的栈顶,顶部的值被丢弃。否则,为了得到指定的大小这个函数压入相应个数的空值//(nil)到栈上。特别的, lua_settop(L,0)清空堆栈。你也可以用负数索引作为调用 lua_settop 的参数;那将会设置栈顶到指定的索引。

//利用这种技巧, API 提供了下面这个宏,它从堆栈中弹出 n 个元素:
//#definelua_pop(L,n) lua_settop(L, -(n)-1)

void lua_settop(lua_State *L, int index);

//函数 lua_pushvalue 压入堆栈上指定索引的一个抟贝到栈顶; lua_remove 移除指定索引位置的元素,并将其上面所有的元素下移来填补这个位置的空白;
void lua_pushvalue(lua_State *L, int index);
void lua_remove(lua_State *L, int index);
void lua_insert(lua_State *L, int index);
void lua_replace(lua_State *L, int index);

lua_insert移动栈顶元素到指定索引的位置,并将这个索引位置上面的元素全部上移至栈顶被移动留下的空隔;最后,lua_replace从栈顶弹出元素值并将其设置到指定索引位置,没有任何移动操作。


注意到下面的操作对堆栈没有任何影响:
lua_settop(L, -1); /* set top to its current value */
lua_insert(L, -1); /* move top element to the top */



demo:

static void stackDump (lua_State *L) 
{
	int i;
	int top = lua_gettop(L);
       //从栈底到栈顶遍历了整个堆栈,依照每个元素自己的类型打印出其值。 
        for (i = 1; i <= top; i++) { /* repeat for each level */
		int t = lua_type(L, i);
		switch (t) 
	{
	case LUA_TSTRING: /* strings */
		printf("`%s'", lua_tostring(L, i));
		break;
	case LUA_TBOOLEAN: /* booleans */
		printf(lua_toboolean(L, i) ? "true" : "false");
		break;
	case LUA_TNUMBER: /* numbers */
		printf("%g", lua_tonumber(L, i));
		break;
	default: /* other values */
		printf("%s", lua_typename(L, t));
		break;
			}
			printf(" "); /* put a separator */
		}
		printf("\n"); /* end the listing */
}


int _tmain(int argc, _TCHAR* argv[])


		lua_State *L = lua_open();
		lua_pushboolean(L, 1); lua_pushnumber(L, 10);
		lua_pushnil(L); lua_pushstring(L, "hello");
		stackDump(L);
		/* true 10 nil `hello' */
		lua_pushvalue(L, -4); stackDump(L);
		/* true 10 nil `hello' true */
		lua_replace(L, 3); stackDump(L);
		/* true 10 true `hello' */
		lua_settop(L, 6); stackDump(L);
		/* true 10 true `hello' nil nil */
		lua_remove(L, -3); stackDump(L);
		/* true 10 true nil nil */
		lua_settop(L, -5); stackDump(L);
		/* true */
		lua_close(L);
		return 0;


}



摘自 《Programming in Lua》,内容过多,适当进行了删减 ,加入自己的理解。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

编程小鱼酱

用心写好每一篇博客

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值