2 回答

TA貢獻1772條經(jīng)驗 獲得超6個贊
你就也定義一個形參中的結(jié)構(gòu)體指針接收傳過來的結(jié)構(gòu)體指針就OK啦
形參中的結(jié)構(gòu)體指針改變?這什么意思?
給你看個例子吧:
struct node *creat(struct node *l)
{
struct node *head;
head=l;
return head;
}
你是這樣嗎~

TA貢獻1842條經(jīng)驗 獲得超21個贊
擴展Lua的基本方法之一就是為應(yīng)用程序注冊新的C函數(shù)到Lua中去。
當(dāng)我們提到Lua可以調(diào)用C函數(shù),不是指Lua可以調(diào)用任何類型的C函數(shù)(有一些包可以讓Lua調(diào)用任意的C函數(shù),但缺乏便捷和健壯性)。正如我們前面所看到的,當(dāng)C調(diào)用Lua函數(shù)的時候,必須遵循一些簡單的協(xié)議來傳遞參數(shù)和獲取返回結(jié)果。相似的,從Lua中調(diào)用C函數(shù),也必須遵循一些協(xié)議來傳遞參數(shù)和獲得返回結(jié)果。另外,從Lua調(diào)用C函數(shù)我們必須注冊函數(shù),也就是說,我們必須把C函數(shù)的地址以一個適當(dāng)?shù)姆绞絺鬟f給Lua解釋器。
當(dāng)Lua調(diào)用C函數(shù)的時候,使用和C調(diào)用Lua相同類型的棧來交互。C函數(shù)從棧中獲取她的參數(shù),調(diào)用結(jié)束后將返回結(jié)果放到棧中。為了區(qū)分返回結(jié)果和棧中的其他的值,每個C函數(shù)還會返回結(jié)果的個數(shù)(the function returns (in C) the number of results it is leaving on the stack.)。這兒有一個重要的概念:用來交互的棧不是全局變量,每一個函數(shù)都有他自己的私有棧。當(dāng)Lua調(diào)用C函數(shù)的時候,第一個參數(shù)總是在這個私有棧的index=1的位置。甚至當(dāng)一個C函數(shù)調(diào)用Lua代碼(Lua代碼調(diào)用同一個C函數(shù)或者其他的C函數(shù)),每一個C函數(shù)都有自己的獨立的私有棧,并且第一個參數(shù)在index=1的位置。
26.1 C 函數(shù)
先看一個簡單的例子,如何實現(xiàn)一個簡單的函數(shù)返回給定數(shù)值的sin值(更專業(yè)的實現(xiàn)應(yīng)該檢查他的參數(shù)是否為一個數(shù)字):
static int l_sin (lua_State *L) {
double d = lua_tonumber(L, 1); /* get argument */
lua_pushnumber(L, sin(d)); /* push result */
return 1; /* number of results */
}
任何在Lua中注冊的函數(shù)必須有同樣的原型,這個原型聲明定義就是lua.h中的lua_CFunction:
typedef int (*lua_CFunction) (lua_State *L);
從C的角度來看,一個C函數(shù)接受單一的參數(shù)Lua state,返回一個表示返回值個數(shù)的數(shù)字。所以,函數(shù)在將返回值入棧之前不需要清理棧,函數(shù)返回之后,Lua自動的清除棧中返回結(jié)果下面的所有內(nèi)容。
我們要想在Lua使用這個函數(shù),還必須首先注冊這個函數(shù)。我們使用lua_pushcfunction來完成這個任務(wù):他獲取指向C函數(shù)的指針,并在Lua中創(chuàng)建一個function類型的值來表示這個函數(shù)。一個quick-and-dirty的解決方案是將這段代碼直接放到lua.c文件中,并在調(diào)用lua_open后面適當(dāng)?shù)奈恢眉由舷旅鎯尚校?br/>lua_pushcfunction(l, l_sin);
lua_setglobal(l, "mysin");
第一行將類型為function的值入棧,第二行將function賦值給全局變量mysin。這樣修改之后,重新編譯Lua,你就可以在你的Lua程序中使用新的mysin函數(shù)了。在下面一節(jié),我們將討論以比較好的方法將新的C函數(shù)添加到Lua中去。
對于稍微專業(yè)點的sin函數(shù),我們必須檢查sin的參數(shù)的類型。有一個輔助庫中的luaL_checknumber函數(shù)可以檢查給定的參數(shù)是否為數(shù)字:當(dāng)有錯誤發(fā)生的時候,將拋出一個錯誤信息;否則返回作為參數(shù)的那個數(shù)字。將上面我們的函數(shù)稍作修改:
static int l_sin (lua_State *L) {
double d = luaL_checknumber(L, 1);
lua_pushnumber(L, sin(d));
return 1; /* number of results */
}
根據(jù)上面的定義,如果你調(diào)用mysin('a'),會得到如下信息:
bad argument #1 to 'mysin' (number expected, got string)
注意看看luaL_checknumber是如何自動使用:參數(shù)number(1),函數(shù)名("mysin"),期望的參數(shù)類型("number"),實際的參數(shù)類型("string")來拼接最終的錯誤信息的。
下面看一個稍微復(fù)雜的例子:寫一個返回給定目錄內(nèi)容的函數(shù)。Lua的標(biāo)準(zhǔn)庫并沒有提供這個函數(shù),因為ANSI C沒有可以實現(xiàn)這個功能的函數(shù)。在這兒,我們假定我們的系統(tǒng)符合POSIX標(biāo)準(zhǔn)。我們的dir函數(shù)接受一個代表目錄路徑的字符串作為參數(shù),以數(shù)組的形式返回目錄的內(nèi)容。比如,調(diào)用dir("/home/lua")可能返回{".", "..", "src", "bin", "lib"}。當(dāng)有錯誤發(fā)生的時候,函數(shù)返回nil加上一個描述錯誤信息的字符串。
#include <dirent.h>
#include <errno.h>
static int l_dir (lua_State *L) {
DIR *dir;
struct dirent *entry;
int i;
const char *path = luaL_checkstring(L, 1);
/* open directory */
dir = opendir(path);
if (dir == NULL) { /* error opening the directory? */
lua_pushnil(L); /* return nil and ... */
lua_pushstring(L, strerror(errno)); /* error message */
return 2; /* number of results */
}
/* create result table */
lua_newtable(L);
i = 1;
while ((entry = readdir(dir)) != NULL) {
lua_pushnumber(L, i++); /* push key */
lua_pushstring(L, entry->d_name); /* push value */
lua_settable(L, -3);
}
closedir(dir);
return 1; /* table is already on top */
}
輔助庫的luaL_checkstring函數(shù)用來檢測參數(shù)是否為字符串,與luaL_checknumber類似。(在極端情況下,上面的l_dir的實現(xiàn)可能會導(dǎo)致小的內(nèi)存泄漏。調(diào)用的三個Lua函數(shù)lua_newtable、lua_pushstring和lua_settable可能由于沒有足夠的內(nèi)存而失敗。其中任何一個調(diào)用失敗都會拋出錯誤并且終止l_dir,這種情況下,不會調(diào)用closedir。正如前面我們所討論過的,對于大多數(shù)程序來說這不算個問題:如果程序?qū)е聝?nèi)存不足,最好的處理方式是立即終止程序。另外,在29章我們將看到另外一種解決方案可以避免這個問題的發(fā)生)
26.2 C 函數(shù)庫
一個Lua庫實際上是一個定義了一系列Lua函數(shù)的chunk,并將這些函數(shù)保存在適當(dāng)?shù)牡胤?,通常作為table的域來保存。Lua的C庫就是這樣實現(xiàn)的。除了定義C函數(shù)之外,還必須定義一個特殊的用來和Lua庫的主chunk通信的特殊函數(shù)。一旦調(diào)用,這個函數(shù)就會注冊庫中所有的C函數(shù),并將他們保存到適當(dāng)?shù)奈恢?。像一個Lua主chunk一樣,她也會初始化其他一些在庫中需要初始化的東西。
Lua通過這個注冊過程,就可以看到庫中的C函數(shù)。一旦一個C函數(shù)被注冊之后并保存到Lua中,在Lua程序中就可以直接引用他的地址(當(dāng)我們注冊這個函數(shù)的時候傳遞給Lua的地址)來訪問這個函數(shù)了。換句話說,一旦C函數(shù)被注冊之后,Lua調(diào)用這個函數(shù)并不依賴于函數(shù)名,包的位置,或者調(diào)用函數(shù)的可見的規(guī)則。通常C庫都有一個外部(public/extern)的用來打開庫的函數(shù)。其他的函數(shù)可能都是私有的,在C中被聲明為static。
當(dāng)你打算使用C函數(shù)來擴展Lua的時候,即使你僅僅只想注冊一個C函數(shù),將你的C代碼設(shè)計為一個庫是個比較好的思想:不久的將來你就會發(fā)現(xiàn)你需要其他的函數(shù)。一般情況下,輔助庫對這種實現(xiàn)提供了幫助。luaL_openlib函數(shù)接受一個C函數(shù)的列表和他們對應(yīng)的函數(shù)名,并且作為一個庫在一個table中注冊所有這些函數(shù)??匆粋€例子,假定我們想用一個我們前面提過的l_dir函數(shù)創(chuàng)建一個庫。首先,我們必須定義庫函數(shù):
static int l_dir (lua_State *L) {
... /* as before */
}
第二步,我們聲明一個數(shù)組,保存所有的函數(shù)和他們對應(yīng)的名字。這個數(shù)組的元素類型為luaL_reg:是一個帶有兩個域的結(jié)構(gòu)體,一個字符串和一個函數(shù)指針。
static const struct luaL_reg mylib [] = {
{"dir", l_dir},
{NULL, NULL} /* sentinel */
};
在我們的例子中,只有一個函數(shù)l_dir需要聲明。注意數(shù)組中最后一對必須是{NULL, NULL},用來表示結(jié)束。第三步,我們使用luaL_openlib聲明主函數(shù):
int luaopen_mylib (lua_State *L) {
luaL_openlib(L, "mylib", mylib, 0);
return 1;
}
luaL_openlib的第二個參數(shù)是庫的名稱。這個函數(shù)按照指定的名字創(chuàng)建(或者reuse)一個表,并使用數(shù)組mylib中的name-function對填充這個表。luaL_openlib還允許我們?yōu)閹熘兴械暮瘮?shù)注冊公共的upvalues。例子中不需要使用upvalues,所以最后一個參數(shù)為0。luaL_openlib返回的時候,將保存庫的表放到棧內(nèi)。luaL_openlib函數(shù)返回1,返回這個值給Lua。(The luaopen_mylib function returns 1 to return this value to Lua)(和Lua庫一樣,這個返回值是可選的,因為庫本身已經(jīng)賦給了一個全局變量。另外,像在Lua標(biāo)準(zhǔn)庫中的一樣,這個返回不會有額外的花費,在有時候可能是有用的。)
完成庫的代碼編寫之后,我們必須將它鏈接到Lua解釋器。最常用的方式使用動態(tài)連接庫,如果你的Lua解釋器支持這個特性的話(我們在8.2節(jié)已經(jīng)討論過了動態(tài)連接庫)。在這種情況下,你必須用你的代碼創(chuàng)建動態(tài)連接庫(windows下.dll文件,linux下.so文件)。到這一步,你就可以在Lua中直接使用loadlib加載你剛才定義的函數(shù)庫了,下面這個調(diào)用:
mylib = loadlib("fullname-of-your-library", "luaopen_mylib")
將luaopen_mylib函數(shù)轉(zhuǎn)換成Lua中的一個C函數(shù),并將這個函數(shù)賦值給mylib(那就是為什么luaopen_mylib必須和其他的C函數(shù)有相同的原型的原因所在)。然后,調(diào)用mylib(),將運行l(wèi)uaopen_mylib打開你定義的函數(shù)庫。
如果你的解釋器不支持動態(tài)鏈接庫,你必須將你的新的函數(shù)庫重新編譯到你的Lua中去。除了這以外,還不要一些方式告訴獨立運行的Lua解釋器,當(dāng)他打開一個新的狀態(tài)的時候必須打開這個新定義的函數(shù)庫。宏定義可以很容易實現(xiàn)這個功能。第一,你必須使用下面的內(nèi)容創(chuàng)建一個頭文件(我們可以稱之為mylib.h):
int luaopen_mylib (lua_State *L);
#define LUA_EXTRALIBS { "mylib", luaopen_mylib },
第一行聲明了打開庫的函數(shù)。第二行定義了一個宏LUA_EXTRALIBS作為函數(shù)數(shù)組的新的入口,當(dāng)解釋器創(chuàng)建新的狀態(tài)的時候會調(diào)用這個宏。(這個函數(shù)數(shù)組的類型為struct luaL_reg[],因此我們需要將名字也放進去)
為了在解釋器中包含這個頭文件,你可以在你的編譯選項中定義一個宏LUA_USERCONFIG。對于命令行的編譯器,你只需添加一個下面這樣的選項即可:
-DLUA_USERCONFIG=\"mylib.h\"
(反斜線防止雙引號被shell解釋,當(dāng)我們在C中指定一個頭文件時,這些引號是必需的。)在一個整合的開發(fā)環(huán)境中,你必須在工程設(shè)置中添加類似的東西。然后當(dāng)你重新編譯lua.c的時候,它包含mylib.h,并且因此在函數(shù)庫的列表中可以用新定義的LUA_EXTRALIBS來打開函數(shù)庫。
- 2 回答
- 0 關(guān)注
- 180 瀏覽
添加回答
舉報