print应该是你学习lua语言学会使用的第一个函数,因为你用lua写下的第一句代码一般就是:
print("Hello World!")
现在我们就来看看lua中的print函数到底是如何实现的。当我们调用lua函数print时,实际调用的是C函数luaB_print,其定义如下:
static int luaB_print (lua_State *L) {
int n = lua_gettop(L); /* number of arguments */
int i;
lua_getglobal(L, "tostring");
for (i=1; i<=n; i++) {
const char *s;
size_t l;
lua_pushvalue(L, -1); /* function to be called */
lua_pushvalue(L, i); /* value to print */
lua_call(L, 1, 1);
s = lua_tolstring(L, -1, &l); /* get result */
if (s == NULL)
return luaL_error(L, "'tostring' must return a string to 'print'");
if (i>1) lua_writestring("\t", 1);
lua_writestring(s, l);
lua_pop(L, 1); /* pop result */
}
lua_writeline();
return 0;
}
luaB_print只接受一个参数lua_State* L。读到这里,你应该大体知道lua和C是如何进行数据交换的:通过栈。 luaB_print第一行代码:
int n = lua_gettop(L); /* number of arguments */
源代码中的注释写得很清楚了,获得栈上的参数数量。lua_gettop只是简单地计算了栈顶到当前函数index的差值,当然这个差值就代表了函数的参数数量。 继续往下看:
lua_getglobal(L, "tostring");// lua-->stack
这行代码又是在做什么呢?很简单,在lua的全局表中找到名为tostring的函数,并将此函数push到栈上。此时栈上的情形大概是这样: 栈底 …, function_index, arg1, …, argn, tostring, 栈顶 接下来是一个for循环。这个循环的目的是依次将tostring函数和一个打印参数入栈,将打印参数转换成字符串并且出栈tostring和参数,然后打印字符串;打印完毕继续将tostring函数和下一个打印参数入栈,直到所有参数打印完毕。 继续分析代码:
lua_pushvalue(L, -1);
这里将栈顶元素push到栈上,也就是说将tostring函数再次push到栈上。栈的示意图: 栈底 …, function_index, arg1, …, argn, tostring, tostring, 栈顶 那么现在栈的最上面就两个tostring了。 再看下一行代码:
lua_pushvalue(L, i);
这里将第i个参数push到栈上。执行完此代码,栈就是这样了: 栈底 …, function_index, arg1, …, argn, tostring, tostring, arg1, 栈顶 继续看:
lua_call(L, 1, 1);// 调用栈顶的函数,参数1个,返回值1个
调用lua_call,执行tostring函数将arg1转换成字符串。转换后,tostring和arg1将出栈,然后字符串将入栈,也就是说调用tostring的结果,占据了tostring和arg1原有的位置。执行完lua_call后,栈是这样: 栈底 …, function_index, arg1, …, argn, tostring, arg1的字符串, 栈顶 然后执行代码:
s = lua_tolstring(L, -1, &l); /* get result */
if (s == NULL)
return luaL_error(L, "'tostring' must return a string to 'print'");
将栈上的字符串取出,并检测是否转换成功。 然后执行代码:
if (i>1)
lua_writestring("\t", 1);
lua_writestring(s, l);
这里判断了一下参数是否不止一个。不止一个时,从第二个参数开始,打印参数前先打印一个制表符,这样打印出比较美观。这里的lua_writestring是一个宏定义,其定义如下:
#define lua_writestring(s,l) fwrite((s), sizeof(char), (l), stdout)
将字符串写入到标准输出流stdout。 然后执行:
lua_pop(L, 1);
参数已经打印完毕,参数就没用了,此时就需要将其出栈。所以执行lua_pop。执行玩lua_pop,进入下一次循环(如果能的话),直到所有参数打印完毕。 for循环执行完后,执行:
lua_writeline();
lua_writeline()本身是一个宏定义:
#define lua_writeline() (lua_writestring("\n", 1), fflush(stdout))
写入一个换行符,并刷新缓冲区。 至此,luaB_print函数就分析完毕了。luaB_print就是简单地打印传入的所有参数,并用制表符对其分割,打印完所有参数后,换行。