• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            每天早晨叫醒你的不是鬧鐘,而是夢想

              C++博客 :: 首頁 :: 聯系 :: 聚合  :: 管理
              62 Posts :: 0 Stories :: 5 Comments :: 0 Trackbacks

            常用鏈接

            留言簿(1)

            我參與的團隊

            搜索

            •  

            最新評論

            閱讀排行榜

            評論排行榜

            2014年2月17日 #

            原文出自程序人生 >> C++中的返回值優化(return value optimization)
            返回值優化(Return Value Optimization,簡稱RVO),是這么一種優化機制:當函數需要返回一個對象的時候,如果自己創建一個臨時對象用戶返回,那么這個臨時對象會消耗一個構造函數(Constructor)的調用、一個復制構造函數的調用(Copy Constructor)以及一個析構函數(Destructor)的調用的代價。而如果稍微做一點優化,就可以將成本降低到一個構造函數的代價,下面是在Visual Studio 2008的Debug模式下做的一個測試:(在GCC下測試的時候可能編譯器自己進行了RVO優化,看不到兩種代碼的區別)

             1 // C++ Return Value Optimization
             2 // 作者:代碼瘋子
             3 // 博客:http://www.programlife.net/
             4 #include <iostream>
             5 using namespace std;
             6  
             7 class Rational
             8 {
             9 public:
            10     Rational(int numerator = 0, int denominator = 1) : 
            11       n(numerator), d(denominator)
            12       {
            13           cout << "Constructor Called" << endl;
            14       }
            15       ~Rational()
            16       {
            17           cout << "Destructor Called" << endl;
            18       }
            19       Rational(const Rational& rhs)
            20       {
            21           this->d = rhs.d;
            22           this->n = rhs.n;
            23           cout << "Copy Constructor Called" << endl;
            24       }
            25       int numerator() const { return n; }
            26       int denominator() const { return d; }
            27 private:
            28     int n, d;
            29 };
            30  
            31 //const Rational operator*(const Rational& lhs,
            32 //                         const Rational& rhs)
            33 //{
            34 //    return Rational(lhs.numerator() * rhs.numerator(),
            35 //                    lhs.denominator() * rhs.denominator());
            36 //}
            37  
            38 const Rational operator*(const Rational& lhs,
            39                          const Rational& rhs)
            40 {
            41     cout << "----------- Enter operator* -----------" << endl;
            42     Rational tmp(lhs.numerator() * rhs.numerator(),
            43         lhs.denominator() * rhs.denominator());
            44     cout << "----------- Leave operator* -----------" << endl;
            45     return tmp;
            46 }
            47  
            48 int main(int argc, char **argv)
            49 {
            50     Rational x(1, 5), y(2, 9);
            51     Rational z = x * y;
            52     cout << "calc result: " << z.numerator() 
            53         << "/" << z.denominator() << endl;
            54  
            55     return 0;
            56 }

            函數輸出截圖如下:
            Return Value Optimization
            可以看到消耗一個構造函數(Constructor)的調用、一個復制構造函數的調用(Copy Constructor)以及一個析構函數(Destructor)的調用的代價。

            而如果把operator*換成另一種形式:

            1 const Rational operator*(const Rational& lhs,
            2                 const Rational& rhs)
            3 {
            4     return Rational(lhs.numerator() * rhs.numerator(),
            5                 lhs.denominator() * rhs.denominator());
            6 }



            就只會消耗一個構造函數的成本了:
            返回值優化
            posted @ 2014-02-17 18:20 沛沛 閱讀(292) | 評論 (0)編輯 收藏

            本文最初發表于程序人生 >> Copy On Write(寫時復制) 作者:代碼瘋子

            Copy On Write(寫時復制)是在編程中比較常見的一個技術,面試中也會偶爾出現(好像Java中就經常有字符串寫時復制的筆試題),今天在看《More Effective C++》的引用計數時就講到了Copy On Write——寫時復制。下面簡單介紹下Copy On Write(寫時復制),我們假設STL中的string支持寫時復制(只是假設,具體未經考證,這里以Mircosoft Visual Studio 6.0為例,如果有興趣,可以自己翻閱源碼)

            Copy On Write(寫時復制)的原理是什么?
            有一定經驗的程序員應該都知道Copy On Write(寫時復制)使用了“引用計數”,會有一個變量用于保存引用的數量。當第一個類構造時,string的構造函數會根據傳入的參數從堆上分配內存,當有其它類需要這塊內存時,這個計數為自動累加,當有類析構時,這個計數會減一,直到最后一個類析構時,此時的引用計數為1或是0,此時,程序才會真正的Free這塊從堆上分配的內存。
            引用計數就是string類中寫時才拷貝的原理!

            什么情況下觸發Copy On Write(寫時復制)
            很顯然,當然是在共享同一塊內存的類發生內容改變時,才會發生Copy On Write(寫時復制)。比如string類的[]、=、+=、+等,還有一些string類中諸如insert、replace、append等成員函數等,包括類的析構時。

            示例代碼:

            // 作者:代碼瘋子 
            // 博客:http://www.programlife.net/
            // 引用計數 & 寫時復制
            #include <iostream>
            #include <string>
            using namespace std;  
            int main(int argc, char **argv)
            {
               string sa = "Copy on write";
               string sb = sa;
               string sc = sb;
               printf("sa char buffer address: 0x%08X\n", sa.c_str());
               printf("sb char buffer address: 0x%08X\n", sb.c_str());
               printf("sc char buffer address: 0x%08X\n", sc.c_str());  
               sc = "Now writing..."; printf("After writing sc:\n");
               printf("sa char buffer address: 0x%08X\n", sa.c_str());
               printf("sb char buffer address: 0x%08X\n", sb.c_str());
               printf("sc char buffer address: 0x%08X\n", sc.c_str());  
               return 0;
            }
            輸出結果如下(VC 6.0):

            Copy On Write(寫時復制)
            可以看到,VC6里面的string是支持寫時復制的,但是我的Visual Studio 2008就不支持這個特性(Debug和Release都是):
            Visual Studio 2008不支持Copy On Write(寫時復制)

            拓展閱讀:(摘自《Windows Via C/C++》5th Edition,不想看英文可以看中文的PDF,中文版第442頁)
            Static Data Is Not Shared by Multiple Instances of an Executable or a DLL

            posted @ 2014-02-17 18:15 沛沛 閱讀(636) | 評論 (0)編輯 收藏

            http://apt.jenslody.de/



            # 打開軟件源配置文件添加下面5行
            sudo gedit /etc/apt/sources.list

            deb http://apt.jenslody.de/ any main
            deb-src http://apt.jenslody.de/ any main
            deb http://apt.jenslody.de/ any release
            deb-src http://apt.jenslody.de/ any release
            deb http://apt.wxwidgets.org/ lenny-wx main

            # 更新軟件源配置文件 和 安裝Key
            sudo apt-get update
            sudo apt-get install jens-lody-debian-keyring
            wget -q http://apt.wxwidgets.org/key.asc -O- | sudo apt-key add -


            這樣只是把軟件源家進去了,并沒有安裝好,所以還要輸入安裝命令
            # 然后輸入下面這個命令開始安裝 codeblocks
            sudo apt-get install codeblocks



            現在安裝好,從編程軟件里開啟CodeBlocks了,是英文的,并且是最近幾日的最版本


            # 你也可以直接下載 CodeBlocks 的二進制發行包,從這個URL進入
            http://apt.jenslody.de/pool/


            # 中文化CodeBlocks 下載這個包,語言文件 Linux下通用的
            http://srgb.googlecode.com/files/CodeBlocks_Linux_Install.zip

            進入壓縮包把語言文件放到桌面 '/locale/zh_CN/CodeBlocks.mo'  
            中文化 codeblocks locale/zh_CN/codeblocks.mo 里的 中文化文件放這里
             '/usr/share/codeblocks/locale/zh_CN/codeblocks.mo'  
            設置權限為所有人可以訪問




            # 使用管理員權限把 語言包 locale 目錄 拉到 /usr/share/codeblocks里
            sudo nautilus /usr/share/codeblocks/
            注意 locale 的權限可能不完整,所以 選住目錄 所有者-群組-其他設定都能訪問文件;
            對包含的文件應用權限;
            進入 /usr/share/codeblocks/locale/zh_CN/ 目錄選兩個文件
            右鍵修改權限 所有者 和 群組 都可以讀寫




            現在安裝的 CodeBlocks 打開是中文里,但是只有基本IDE環境,很多插件和開發包沒安裝
            可以輸入 sudo apt-get install codeblocks <按兩下TAB>
            列出沒有安裝的其他包, 你可以選擇安裝,我偷懶了
            sudo apt-get install codeblocks* <回車>
            sudo apt-get install libwxsmith* <回車>
            sudo apt-get install libwxgtk2.8-dev <回車>


            現在開啟CB,建立一個wx項目,編譯,可以編譯成功了
            posted @ 2014-02-17 18:10 沛沛 閱讀(246) | 評論 (0)編輯 收藏

               在Lua中可以通過自定義類型的方式與C語言代碼更高效、更靈活的交互。這里我們通過一個簡單完整的示例來學習一下Lua中userdata的使用方式。需要說明的是,該示例完全來自于Programming in Lua。其功能是用C程序實現一個Lua的布爾數組,以提供程序的執行效率。見下面的代碼和關鍵性注釋。   

            復制代碼
              1 #include <lua.hpp>   
            2
            #include <lauxlib.h>
            3
            #include <lualib.h>
            4
            #include <limits.h>
            5

            6
            #define BITS_PER_WORD (CHAR_BIT * sizeof(int))
            7
            #define I_WORD(i) ((unsigned int)(i))/BITS_PER_WORD
            8
            #define I_BIT(i) (1 << ((unsigned int)(i)%BITS_PER_WORD))
            9

            10
            typedef struct NumArray {
            11
            int size; 12 unsigned int values[1];
            13
            } NumArray;
            14

            15
            extern "C" int newArray(lua_State* L)
            16
            {
            17
            //1. 檢查第一個參數是否為整型。以及該參數的值是否大于等于1.
            18
            int n = luaL_checkint(L,1);
            19
            luaL_argcheck(L, n >= 1, 1, "invalid size.");
            20
            size_t nbytes = sizeof(NumArray) + I_WORD(n - 1) * sizeof(int);
            21
            //2. 參數表示Lua為userdata分配的字節數。同時將分配后的userdata對象壓入棧中。
            22
            NumArray* a = (NumArray*)lua_newuserdata(L,nbytes);
            23
            a->size = n;
            24
            for (int i = 0; i < I_WORD(n - 1); ++i)
            25
            a->values[i] = 0;
            26
            //獲取注冊表變量myarray,該key的值為metatable。
            27
            luaL_getmetatable(L,"myarray");
            28
            //將userdata的元表設置為和myarray關聯的table。同時將棧頂元素彈出。
            29
            lua_setmetatable(L,-2);
            30
            return 1;
            31
            }
            32

            33
            extern "C" int setArray(lua_State* L)
            34
            {
            35
            //1. Lua傳給該函數的第一個參數必須是userdata,該對象的元表也必須是注冊表中和myarray關聯的table。
            36
            //否則該函數報錯并終止程序。
            37
            NumArray* a = (NumArray*)luaL_checkudata(L,1,"myarray");
            38
            int index = luaL_checkint(L,2) - 1;
            39
            //2. 由于任何類型的數據都可以成為布爾值,因此這里使用any只是為了確保有3個參數。
            40
            luaL_checkany(L,3);
            41
            luaL_argcheck(L,a != NULL,1,"'array' expected.");
            42
            luaL_argcheck(L,0 <= index && index < a->size,2,"index out of range.");
            43
            if (lua_toboolean(L,3))
            44
            a->values[I_WORD(index)] |= I_BIT(index);
            45
            else
            46
            a->values[I_WORD(index)] &= ~I_BIT(index);
            47
            return 0;
            48
            }
            49

            50
            extern "C" int getArray(lua_State* L)
            51
            {
            52
            NumArray* a = (NumArray*)luaL_checkudata(L,1,"myarray");
            53
            int index = luaL_checkint(L,2) - 1;
            54
            luaL_argcheck(L, a != NULL, 1, "'array' expected.");
            55
            luaL_argcheck(L, 0 <= index && index < a->size,2,"index out of range");
            56
            lua_pushboolean(L,a->values[I_WORD(index)] & I_BIT(index));
            57
            return 1;
            58
            }
            59

            60
            extern "C" int getSize(lua_State* L)
            61
            {
            62
            NumArray* a = (NumArray*)luaL_checkudata(L,1,"myarray");
            63
            luaL_argcheck(L,a != NULL,1,"'array' expected.");
            64
            lua_pushinteger(L,a->size);
            65
            return 1;
            66
            }
            67

            68
            extern "C" int array2string(lua_State* L)
            69
            {
            70
            NumArray* a = (NumArray*)luaL_checkudata(L,1,"myarray");
            71
            lua_pushfstring(L,"array(%d)",a->size);
            72
            return 1;
            73
            }
            74

            75
            static luaL_Reg arraylib_f [] = {
            76
            {"new", newArray},
            77
            {NULL, NULL}
            78
            };
            79

            80
            static luaL_Reg arraylib_m [] = {
            81
            {"set", setArray},
            82
            {"get", getArray},
            83
            {"size", getSize},
            84
            {"__tostring", array2string}, //print(a)時Lua會調用該元方法。
            85
            {NULL, NULL}
            86
            };
            87

            88
            extern "C" __declspec(dllexport)
            89
            int luaopen_testuserdata(lua_State* L)
            90
            {
            91
            //1. 創建元表,并將該元表指定給newArray函數新創建的userdata。在Lua中userdata也是以table的身份表現的。
            92
            //這樣在調用對象函數時,可以通過驗證其metatable的名稱來確定參數userdata是否合法。
            93
            luaL_newmetatable(L,"myarray");
            94
            lua_pushvalue(L,-1);
            95 //2. 為了實現面對對象的調用方式,需要將元表的__index字段指向自身,同時再將arraylib_m數組中的函數注冊到
            96
            //元表中,之后基于這些注冊函數的調用就可以以面向對象的形式調用了。
            97
            //lua_setfield在執行后會將棧頂的table彈出。
            98
            lua_setfield(L,-2,"__index");
            99
            //將這些成員函數注冊給元表,以保證Lua在尋找方法時可以定位。NULL參數表示將用棧頂的table代替第二個參數。
            100 luaL_register(L,NULL,arraylib_m);
            101 //這里只注冊的工廠方法。
            102 luaL_register(L,"testuserdata",arraylib_f);
            103 return 1;
            104 }
            復制代碼

              輕量級userdata: 
              之前介紹的是full userdata,Lua還提供了另一種輕量級userdata(light userdata)。事實上,輕量級userdata僅僅表示的是C指針的值,即(void*)。由于它只是一個值,所以不用創建。如果需要將一個輕量級userdata放入棧中,調用lua_pushlightuserdata即可。full userdata和light userdata之間最大的區別來自于相等性判斷,對于一個full userdata,它只是與自身相等,而light userdata則表示為一個C指針,因此,它與所有表示同一指針的light userdata相等。再有就是light userdata不會受到垃圾收集器的管理,使用時就像一個普通的整型數字一樣。

            posted @ 2014-02-17 17:50 沛沛 閱讀(468) | 評論 (0)編輯 收藏

                 摘要:  1. 數組操作:    在Lua中,“數組”只是table的一個別名,是指以一種特殊的方法來使用table。出于性能原因,Lua的C API為數組操作提供了專門的函數,如:    void lua_rawgeti(lua_State* L, int index, int key);  ...  閱讀全文
            posted @ 2014-02-17 17:45 沛沛 閱讀(266) | 評論 (0)編輯 收藏

             Lua可以調用C函數的能力將極大的提高Lua的可擴展性和可用性。對于有些和操作系統相關的功能,或者是對效率要求較高的模塊,我們完全可以通過C函數來實現,之后再通過Lua調用指定的C函數。對于那些可被Lua調用的C函數而言,其接口必須遵循Lua要求的形式,即typedef int (*lua_CFunction)(lua_State* L)。簡單說明一下,該函數類型僅僅包含一個表示Lua環境的指針作為其唯一的參數,實現者可以通過該指針進一步獲取Lua代碼中實際傳入的參數。返回值是整型,表示該C函數將返回給Lua代碼的返回值數量,如果沒有返回值,則return 0即可。需要說明的是,C函數無法直接將真正的返回值返回給Lua代碼,而是通過虛擬棧來傳遞Lua代碼和C函數之間的調用參數和返回值的。這里我們將介紹兩種Lua調用C函數的規則。
                1. C函數作為應用程序的一部分。

            復制代碼
            1 #include <stdio.h>  
            2
            #include <string.h>
            3
            #include <lua.hpp>
            4
            #include <lauxlib.h>
            5
            #include <lualib.h>
            6

            7
            //待Lua調用的C注冊函數。
            8
            static int add2(lua_State* L)
            9
            {
            10 //檢查棧中的參數是否合法,1表示Lua調用時的第一個參數(從左到右),依此類推。
            11 //如果Lua代碼在調用時傳遞的參數不為number,該函數將報錯并終止程序的執行。
            12 double op1 = luaL_checknumber(L,1);
            13 double op2 = luaL_checknumber(L,2);
            14 //將函數的結果壓入棧中。如果有多個返回值,可以在這里多次壓入棧中。
            15 lua_pushnumber(L,op1 + op2);
            16 //返回值用于提示該C函數的返回值數量,即壓入棧中的返回值數量。
            17 return 1;
            18 }
            19
            20 //另一個待Lua調用的C注冊函數。
            21 static int sub2(lua_State* L)
            22 {
            23 double op1 = luaL_checknumber(L,1);
            24 double op2 = luaL_checknumber(L,2);
            25 lua_pushnumber(L,op1 - op2);
            26 return 1;
            27 }
            28
            29 const char* testfunc = "print(add2(1.0,2.0)) print(sub2(20.1,19))";
            30
            31 int main()
            32 {
            33 lua_State* L = luaL_newstate();
            34 luaL_openlibs(L);
            35 //將指定的函數注冊為Lua的全局函數變量,其中第一個字符串參數為Lua代碼
            36 //在調用C函數時使用的全局函數名,第二個參數為實際C函數的指針。
            37 lua_register(L, "add2", add2);
            38 lua_register(L, "sub2", sub2);
            39 //在注冊完所有的C函數之后,即可在Lua的代碼塊中使用這些已經注冊的C函數了。
            40 if (luaL_dostring(L,testfunc))
            41 printf("Failed to invoke.\n");
            42 lua_close(L); 43 return 0; 44 }
            復制代碼

                2. C函數庫成為Lua的模塊。
                將包含C函數的代碼生成庫文件,如Linux的so,或Windows的DLL,同時拷貝到Lua代碼所在的當前目錄,或者是LUA_CPATH環境變量所指向的目錄,以便于Lua解析器可以正確定位到他們。在我當前的Windows系統中,我將其copy到"C:\Program Files\Lua\5.1\clibs\",這里包含了所有Lua可調用的C庫。見如下C語言代碼和關鍵性注釋:

            復制代碼
            1 #include <stdio.h>  
            2
            #include <string.h>
            3
            #include <lua.hpp>
            4
            #include <lauxlib.h>
            5
            #include <lualib.h>
            6

            7
            //待注冊的C函數,該函數的聲明形式在上面的例子中已經給出。
            8
            //需要說明的是,該函數必須以C的形式被導出,因此extern "C"是必須的。
            9
            //函數代碼和上例相同,這里不再贅述。
            10 extern "C" int add(lua_State* L)
            11 { 12 double op1 = luaL_checknumber(L,1);
            13 double op2 = luaL_checknumber(L,2);
            14 lua_pushnumber(L,op1 + op2);
            15 return 1;
            16 }
            17
            18 extern "C" int sub(lua_State* L)
            19 {
            20 double op1 = luaL_checknumber(L,1);
            21 double op2 = luaL_checknumber(L,2);
            22 lua_pushnumber(L,op1 - op2);
            23 return 1;
            24 }
            25
            26 //luaL_Reg結構體的第一個字段為字符串,在注冊時用于通知Lua該函數的名字。
            27 //第一個字段為C函數指針。
            28 //結構體數組中的最后一個元素的兩個字段均為NULL,用于提示Lua注冊函數已經到達數組的末尾。
            29 static luaL_Reg mylibs[] = {
            30 {"add", add},
            31 {"sub", sub},
            32 {NULL, NULL}
            33 };
            34
            35 //該C庫的唯一入口函數。其函數簽名等同于上面的注冊函數。見如下幾點說明:
            36 //1. 我們可以將該函數簡單的理解為模塊的工廠函數。
            37 //2. 其函數名必須為luaopen_xxx,其中xxx表示library名稱。Lua代碼require "xxx"需要與之對應。
            38 //3. 在luaL_register的調用中,其第一個字符串參數為模塊名"xxx",第二個參數為待注冊函數的數組。
            39 //4. 需要強調的是,所有需要用到"xxx"的代碼,不論C還是Lua,都必須保持一致,這是Lua的約定,
            40 // 否則將無法調用。
            41 extern "C" __declspec(dllexport)
            42 int luaopen_mytestlib(lua_State* L)
            43 {
            44 const char* libName = "mytestlib";
            45 luaL_register(L,libName,mylibs);
            46 return 1;
            47 }
            復制代碼

                見如下Lua代碼:

            1 require "mytestlib"  --指定包名稱 
            2
            3 --在調用時,必須是package.function
            4 print(mytestlib.add(1.0,2.0))
            5 print(mytestlib.sub(20.1,19))
            posted @ 2014-02-17 17:45 沛沛 閱讀(299) | 評論 (0)編輯 收藏

                 摘要:  1. 基礎:    Lua的一項重要用途就是作為一種配置語言。現在從一個簡單的示例開始吧。    --這里是用Lua代碼定義的窗口大小的配置信息    width = 200    height = 300    下面是讀取配置信息的C/...  閱讀全文
            posted @ 2014-02-17 17:44 沛沛 閱讀(353) | 評論 (0)編輯 收藏

               Lua是一種嵌入式腳本語言,即Lua不是可以單獨運行的程序,在實際應用中,主要存在兩種應用形式。第一種形式是,C/C++作為主程序,調用Lua代碼,此時可以將Lua看做“可擴展的語言”,我們將這種應用稱為“應用程序代碼”。第二種形式是Lua具有控制權,而C/C++代碼則作為Lua的“庫代碼”。在這兩種形式中,都是通過Lua提供的C API完成兩種語言之間的通信的。

                1. 基礎知識:
                C API是一組能使C/C++代碼與Lua交互的函數。其中包括讀寫Lua全局變量、調用Lua函數、運行一段Lua代碼,以及注冊C函數以供Lua代碼調用等。這里先給出一個簡單的示例代碼:

            復制代碼
             1 #include <stdio.h>
            2 #include <string.h>
            3 #include <lua.hpp>
            4 #include <lauxlib.h>
            5 #include <lualib.h>
            6
            7 int main(void)
            8 {
            9 const char* buff = "print(\"hello\")";
            10 int error;
            11 lua_State* L = luaL_newstate();
            12 luaL_openlibs(L);
            13
            14 error = luaL_loadbuffer(L,buff,strlen(buff),"line") || lua_pcall(L,0,0,0);
            15 int s = lua_gettop(L);
            16 if (error) {
            17 fprintf(stderr,"%s",lua_tostring(L,-1));
            18 lua_pop(L,1);
            19 }
            20 lua_close(L);
            21 return 0;
            22 }
            復制代碼

                下面是針對以上代碼給出的具體解釋:
                1). 上面的代碼是基于我的C++工程,而非C工程,因此包含的頭文件是lua.hpp,如果是C工程,可以直接包含lua.h。
                2). Lua庫中沒有定義任何全局變量,而是將所有的狀態都保存在動態結構lua_State中,后面所有的C API都需要該指針作為第一個參數。
                3). luaL_openlibs函數是用于打開Lua中的所有標準庫,如io庫、string庫等。
                4). luaL_loadbuffer編譯了buff中的Lua代碼,如果沒有錯誤,則返回0,同時將編譯后的程序塊壓入虛擬棧中。
                5). lua_pcall函數會將程序塊從棧中彈出,并在保護模式下運行該程序塊。執行成功返回0,否則將錯誤信息壓入棧中。
                6). lua_tostring函數中的-1,表示棧頂的索引值,棧底的索引值為1,以此類推。該函數將返回棧頂的錯誤信息,但是不會將其從棧中彈出。
                7). lua_pop是一個宏,用于從虛擬棧中彈出指定數量的元素,這里的1表示僅彈出棧頂的元素。
                8). lua_close用于釋放狀態指針所引用的資源。

                2. 棧:
                在Lua和C語言之間進行數據交換時,由于兩種語言之間有著較大的差異,比如Lua是動態類型,C語言是靜態類型,Lua是自動內存管理,而C語言則是手動內存管理。為了解決這些問題,Lua的設計者使用了虛擬棧作為二者之間數據交互的介質。在C/C++程序中,如果要獲取Lua的值,只需調用Lua的C API函數,Lua就會將指定的值壓入棧中。要將一個值傳給Lua時,需要先將該值壓入棧,然后調用Lua的C API,Lua就會獲取該值并將其從棧中彈出。為了可以將不同類型的值壓入棧,以及從棧中取出不同類型的值,Lua為每種類型均設定了一個特定函數。
                1). 壓入元素:
                Lua針對每種C類型,都有一個C API函數與之對應,如:
                void lua_pushnil(lua_State* L);  --nil值
                void lua_pushboolean(lua_State* L, int b); --布爾值
                void lua_pushnumber(lua_State* L, lua_Number n); --浮點數
                void lua_pushinteger(lua_State* L, lua_Integer n);  --整型
                void lua_pushlstring(lua_State* L, const char* s, size_t len); --指定長度的內存數據
                void lua_pushstring(lua_State* L, const char* s);  --以零結尾的字符串,其長度可由strlen得出。
                對于字符串數據,Lua不會持有他們的指針,而是調用在API時生成一個內部副本,因此,即使在這些函數返回后立刻釋放或修改這些字符串指針,也不會有任何問題。
                在向棧中壓入數據時,可以通過調用下面的函數判斷是否有足夠的棧空間可用,一般而言,Lua會預留20個槽位,對于普通應用來說已經足夠了,除非是遇到有很多參數的函數。
                int lua_checkstack(lua_State* L, int extra) --期望得到extra數量的空閑槽位,如果不能擴展并獲得,返回false。 
                
                2). 查詢元素:
                API使用“索引”來引用棧中的元素,第一個壓入棧的為1,第二個為2,依此類推。我們也可以使用負數作為索引值,其中-1表示為棧頂元素,-2為棧頂下面的元素,同樣依此類推。
                Lua提供了一組特定的函數用于檢查返回元素的類型,如:
                int lua_isboolean (lua_State *L, int index);
                int lua_iscfunction (lua_State *L, int index);
                int lua_isfunction (lua_State *L, int index);
                int lua_isnil (lua_State *L, int index);
                int lua_islightuserdata (lua_State *L, int index);
                int lua_isnumber (lua_State *L, int index);
                int lua_isstring (lua_State *L, int index);
                int lua_istable (lua_State *L, int index);
                int lua_isuserdata (lua_State *L, int index);
                以上函數,成功返回1,否則返回0。需要特別指出的是,對于lua_isnumber而言,不會檢查值是否為數字類型,而是檢查值是否能轉換為數字類型。
                Lua還提供了一個函數lua_type,用于獲取元素的類型,函數原型如下:
                int lua_type (lua_State *L, int index);
                該函數的返回值為一組常量值,分別是:LUA_TNIL、LUA_TNUMBER、LUA_TBOOLEAN、LUA_TSTRING、LUA_TTABLE、LUA_TFUNCTION、LUA_TUSERDATA、LUA_TTHREAD和LUA_TLIGHTUSERDATA。這些常量通常用于switch語句中。
                除了上述函數之外,Lua還提供了一組轉換函數,如:
                int lua_toboolean (lua_State *L, int index);
                lua_CFunction lua_tocfunction (lua_State *L, int index);
                lua_Integer lua_tointeger (lua_State *L, int index);    
                const char *lua_tolstring (lua_State *L, int index, size_t *len);
                lua_Number lua_tonumber (lua_State *L, int index);
                const void *lua_topointer (lua_State *L, int index);
                const char *lua_tostring (lua_State *L, int index);
                void *lua_touserdata (lua_State *L, int index);
                --string類型返回字符串長度,table類型返回操作符'#'等同的結果,userdata類型返回分配的內存塊長度。
                size_t lua_objlen (lua_State *L, int index); 
                對于上述函數,如果調用失敗,lua_toboolean、lua_tonumber、lua_tointeger和lua_objlen均返回0,而其他函數則返回NULL。在很多時候0不是一個很有效的用于判斷錯誤的值,但是ANSI C沒有提供其他可以表示錯誤的值。因此對于這些函數,在有些情況下需要先使用lua_is*系列函數判斷是否類型正確,而對于剩下的函數,則可以直接通過判斷返回值是否為NULL即可。
                對于lua_tolstring函數返回的指向內部字符串的指針,在該索引指向的元素被彈出之后,將無法保證仍然有效。該函數返回的字符串末尾均會有一個尾部0。
                下面將給出一個工具函數,可用于演示上面提到的部分函數,如:

            復制代碼
             1 static void stackDump(lua_State* L) 
            2 {
            3 int top = lua_gettop(L);
            4 for (int i = 1; i <= top; ++i) {
            5 int t = lua_type(L,i);
            6 switch(t) {
            7 case LUA_TSTRING:
            8 printf("'%s'",lua_tostring(L,i));
            9 break;
            10 case LUA_TBOOLEAN:
            11 printf(lua_toboolean(L,i) ? "true" : "false");
            12 break;
            13 case LUA_TNUMBER:
            14 printf("%g",lua_tonumber(L,i));
            15 break;
            16 default:
            17 printf("%s",lua_typename(L,t));
            18 break;
            19 }
            20 printf("");
            21 }
            22 printf("\n");
            23 }
            復制代碼

                3). 其它棧操作函數:
                除了上面給出的數據交換函數之外,Lua的C API還提供了一組用于操作虛擬棧的普通函數,如:
                int lua_gettop(lua_State* L); --返回棧中元素的個數。
                void lua_settop(lua_State* L, int index); --將棧頂設置為指定的索引值。
                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還提供了一個宏用于彈出指定數量的元素:#define lua_pop(L,n)  lua_settop(L, -(n) - 1)    
                見如下示例代碼:

            復制代碼
             1 int main()
            2 {
            3 lua_State* L = luaL_newstate();
            4 lua_pushboolean(L,1);
            5 lua_pushnumber(L,10);
            6 lua_pushnil(L);
            7 lua_pushstring(L,"hello");
            8 stackDump(L); //true 10 nil 'hello'
            9
            10 lua_pushvalue(L,-4);
            11 stackDump(L); //true 10 nil 'hello' true
            12
            13 lua_replace(L,3);
            14 stackDump(L); //true 10 true 'hello'
            15
            16 lua_settop(L,6);
            17 stackDump(L); //true 10 true 'hello' nil nil
            18
            19 lua_remove(L,-3);
            20 stackDump(L); //true 10 true nil nil
            21
            22 lua_settop(L,-5);
            23 stackDump(L); //true
            24
            25 lua_close(L);
            26 return 0;
            27 }
            復制代碼


                3. C API中的錯誤處理:
                1). C程序調用Lua代碼的錯誤處理:
                通常情況下,應用程序代碼是以“無保護”模式運行的。因此,當Lua發現“內存不足”這類錯誤時,只能通過調用“緊急”函數來通知C語言程序,之后在結束應用程序。用戶可通過lua_atpanic來設置自己的“緊急”函數。如果希望應用程序代碼在發生Lua錯誤時不會退出,可通過調用lua_pcall函數以保護模式運行Lua代碼。這樣再發生內存錯誤時,lua_pcall會返回一個錯誤代碼,并將解釋器重置為一致的狀態。如果要保護與Lua的C代碼,可以使用lua_cpall函數,它將接受一個C函數作為參數,然后調用這個C函數。
                
                2). Lua調用C程序:
                通常而言,當一個被Lua調用的C函數檢測到錯誤時,它就應該調用lua_error,該函數會清理Lua中所有需要清理的資源,然后跳轉回發起執行的那個lua_pcall,并附上一條錯誤信息。

            posted @ 2014-02-17 17:44 沛沛 閱讀(315) | 評論 (0)編輯 收藏

              Lua為了保證高度的可移植性,因此,它的標準庫僅僅提供了非常少的功能,特別是和OS相關的庫。但是Lua還提供了一些擴展庫,比如Posix庫等。對于文件操作而言,該庫僅提供了os.rename函數和os.remove函數。
                
                1. 日期和時間:
                在Lua中,函數timedate提供了所有的日期和時間功能。
                如果不帶任何參數調用time函數,它將以數字形式返回當前的日期和時間。如果以一個table作為參數,它將返回一個數字,表示該table中所描述的日期和時間。該table的有效字段如下:

            字段名描述
            year一個完整的年份
            month01-12
            day01-31
            hour00-23
            min00-59
            sec00-59
            isdst布爾值,true表示夏令時

                print(os.time{year = 1970, month = 1, day = 1, hour = 8, min = 0}) --北京是東八區,所以hour等于時表示UTC的0。
                print(os.time())  --輸出當前時間距離1970-1-1 00:00:00所經過的秒數。輸出值為 1333594721
                函數date是time的反函數,即可以將time返回的數字值轉換為更高級的可讀格式,其第一個參數是格式化字符串,表示期望的日期返回格式,第二個參數是日期和時間的數字,缺省為當前日期和時間。如:

            復制代碼
             1 dd = os.date("*t",os.time())  --如果格式化字符串為"*t",函數將返回table形式的日期對象。如果為"!*t",則表示為UTC時間格式。
            2 print("year = " .. dd.year)
            3 print("month = " .. dd.month)
            4 print("day = " .. dd.day)
            5 print("weekday = " .. dd.wday) --一個星期中的第幾天,周日是第一天
            6 print("yearday = " .. dd.yday) --一年中的第幾天,1月1日是第一天
            7 print("hour = " .. dd.hour)
            8 print("min = " .. dd.min)
            9 print("sec = " .. dd.sec)
            10
            11 --[[
            12 year = 2012
            13 month = 4
            14 day = 5
            15 weekday = 5
            16 yearday = 96
            17 hour = 11
            18 min = 13
            19 sec = 44
            20 --]]
            復制代碼

                date函數的格式化標識和C運行時庫中的strftime函數的標識完全相同,見下表:

            關鍵字描述
            %a一星期中天數的縮寫,如Wed
            %A一星期中天數的全稱,如Friday
            %b月份的縮寫,如Sep
            %B月份的全稱,如September
            %c日期和時間
            %d一個月中的第幾天(01-31)
            %H24小時制中的小時數(00-23)
            %I12小時制中的小時數(01-12)
            %j一年中的第幾天(001-366)
            %M分鐘(00-59)
            %m月份(01-12)
            %p"上午(am)"或"下午(pm)"
            %S秒數(00-59)
            %w一星期中的第幾天(0--6等價于星期日--星期六)
            %x日期,如09/16/2010
            %X時間,如23:48:20
            %y兩位數的年份(00-99)
            %Y完整的年份(2012)
            %%字符'%'

                print(os.date("%Y-%m-%d"))  --輸出2012-04-05
                函數os.clock()返回CPU時間的描述,通常用于計算一段代碼的執行效率。如:

            復制代碼
            1 local x = os.clock()
            2 local s = 0
            3 for i = 1, 10000000 do
            4 s = s + i
            5 end
            6 print(string.format("elapsed time: %.2f\n", os.clock() - x))
            7
            8 --輸出結果為:
            9 --elapsed time: 0.21
            復制代碼


                2. 其他系統調用:
                函數os.exit()可中止當前程序的執行。函數os.getenv()可獲取一個環境變量的值。如:
                print(os.getenv("PATH"))  --如果環境變量不存在,返回nil。
                os.execute函數用于執行和操作系統相關的命令,如:
                os.execute("mkdir " .. "hello")

            posted @ 2014-02-17 17:43 沛沛 閱讀(279) | 評論 (0)編輯 收藏

              I/O庫為文件操作提供了兩種不同的模型,簡單模型和完整模型。簡單模型假設一個當前輸入文件和一個當前輸出文件,他的I/O操作均作用于這些文件。完整模型則使用顯式的文件句柄,并將所有的操作定義為文件句柄上的方法。
                1. 簡單模型:
                I/O庫會將進程標準輸入輸出作為其缺省的輸入文件和輸出文件。我們可以通過io.input(filename)io.output(filename)這兩個函數來改變當前的輸入輸出文件。
                1). io.write函數:
                函數原型為io.write(...)。該函數將所有參數順序的寫入到當前輸出文件中。如:
                io.write("hello","world") --寫出的內容為helloworld

                2). io.read函數:
                下表給出了該函數參數的定義和功能描述:

            參數字符串含義
            "*all"讀取整個文件
            "*line"讀取下一行
            "*number"讀取一個數字
            <num>讀取一個不超過<num>個字符的字符串

                調用io.read("*all")會讀取當前輸入文件的所有內容,以當前位置作為開始。如果當前位置處于文件的末尾,或者文件為空,那個該調用返回一個空字符串。由于Lua可以高效的處理長字符串,因此在Lua中可以先將數據從文件中完整讀出,之后在通過Lua字符串庫提供的函數進行各種處理。
                調用io.read("*line")會返回當前文件的下一行,但不包含換行符。當到達文件末尾時,該調用返回nil。如:

            復制代碼
            1 for count = 1,math.huge do
            2 local line = io.read("*line") --如果不傳參數,缺省值也是"*line"
            3 if line == nil then
            4 break
            5 end
            6 io.write(string.format("%6d ",count),line,"\n")
            7 end
            復制代碼

                如果只是為了迭代文件中的所有行,可以使用io.lines函數,以迭代器的形式訪問文件中的每一行數據,如:

            復制代碼
            1 local lines = {}
            2 for line in io.lines() do --通過迭代器訪問每一個數據
            3 lines[#lines + 1] = line
            4 end
            5 table.sort(lines) --排序,Lua標準庫的table庫提供的函數。
            6 for _,l in ipairs(lines) do
            7 io.write(l,"\n")
            8 end
            復制代碼

                調用io.read("*number")會從當前輸入文件中讀取一個數字。此時read將直接返回一個數字,而不是字符串。"*number"選項會忽略數字前面所有的空格,并且能處理像-3、+5.2這樣的數字格式。如果當前讀取的數據不是合法的數字,read返回nil。在調用read是可以指定多個選項,函數會根據每個選項參數返回相應的內容。如:

            復制代碼
             1 --[[
            2 6.0 -3.23 1000
            3 ... ...
            4 下面的代碼讀取注釋中的數字
            5 --]]
            6 while true do
            7 local n1,n2,n3 = io.read("*number","*number","*number")
            8 if not n1 then
            9 break
            10 end
            11 print(math.max(n1,n2,n3))
            12 end
            復制代碼

                調用io.read(<num>)會從輸入文件中最多讀取n個字符,如果讀不到任何字符,返回nil。否則返回讀取到的字符串。如:

            復制代碼
            1 while true do
            2 local block = io.read(2^13)
            3 if not block then
            4 break
            5 end
            6 io.write(block)
            7 end
            復制代碼

                io.read(0)是一種特殊的情況,用于檢查是否到達了文件的末尾。如果沒有到達,返回空字符串,否則nil。

                2. 完整I/O模型:
                Lua中完整I/O模型的使用方式非常類似于C運行時庫的文件操作函數,它們都是基于文件句柄的。
                1). 通過io.open函數打開指定的文件,并且在參數中給出對該文件的打開模式,其中"r"表示讀取,"w"表示覆蓋寫入,即先刪除文件原有的內容,"a"表示追加式寫入,"b"表示以二進制的方式打開文件。在成功打開文件后,該函數將返回表示該文件的句柄,后面所有基于該文件的操作,都需要將該句柄作為參數傳入。如果打開失敗,返回nil。其中錯誤信息由該函數的第二個參數返回,如:
                assert(io.open(filename,mode))  --如果打開失敗,assert將打印第二個參數給出的錯誤信息。
                
                2). 文件讀寫函數read/write。這里需要用到冒號語法,如:

            1 local f = assert(io.open(filename,"r"))
            2 local t = f:read("*all") --對于read而言,其參數完全等同于簡單模型下read的參數。
            3 f:close()

                此外,I/O庫還提供了3個預定義的文件句柄,即io.stdin(標準輸入)、io.stdout(標準輸出)、io.stderr(標準錯誤輸出)。如:
                io.stderr:write("This is an error message.")
                事實上,我們也可以混合使用簡單模式和完整模式,如:

            1 local temp = io.input()   --將當前文件句柄保存
            2 io.input("newInputfile") --打開新的輸入文件
            3 io.input():close() --關閉當前文件
            4 io.input(temp) --恢復原來的輸入文件


                3). 性能小技巧:
                由于一次性讀取整個文件比逐行讀取要快一些,但對于較大的文件,這樣并不可行,因此Lua提供了一種折中的方式,即一次讀取指定字節數量的數據,如果當前讀取中的最后一行不是完整的一行,可通過該方式將該行的剩余部分也一并讀入,從而保證本次讀取的數據均為整行數據,以便于上層邏輯的處理。如:
                local lines,rest = f:read(BUFSIZE,"*line") --rest變量包含最后一行中沒有讀取的部分。
                下面是Shell中wc命令的一個簡單實現。

            復制代碼
             1 local BUFSIZE = 8192
            2 local f = io.input(arg[1]) --打開輸入文件
            3 local cc, lc, wc, = 0, 0, 0 --分別計數字符、行和單詞
            4 while true do
            5 local lines,rest = f:read(BUFSIZE,"*line")
            6 if not lines then
            7 break
            8 end
            9 if rest then
            10 lines = lines .. rest .. "\n"
            11 end
            12 cc = cc + #lines
            13 --計算單詞數量
            14 local _, t = string.gsub(lines."%S+","")
            15 wc = wc + t
            16 --計算行數
            17 _,t = string.gsub(line,"\n","\n")
            18 lc = lc + t
            19 end
            20 print(lc,wc,cc)
            復制代碼


                4). 其它文件操作:
                如io.flush函數會將io緩存中的數據刷新到磁盤文件上。io.close函數將關閉當前打開的文件。io.seek函數用于設置或獲取當前文件的讀寫位置,其函數原型為f:seek(whence,offset),如果whence的值為"set",offset的值則表示為相對于文件起始位置的偏移量。如為"cur"(默認值),offset則為相對于當前位置的偏移量,如為"end",則為相對于文件末尾的偏移量。函數的返回值與whence參數無關,總是返回文件的當前位置,即相對于文件起始處的偏移字節數。offset的默認值為0。如:

            1 function fsize(file)
            2 local current = file:seek() --獲取當前位置
            3 local size = file:seek("end") --獲取文件大小
            4 file:seek("set",current) --恢復原有的當前位置
            5 return size
            6 end

                最后需要指出的是,如果發生錯誤,所有這些函數均返回nil和一條錯誤信息。

            posted @ 2014-02-17 17:43 沛沛 閱讀(278) | 評論 (0)編輯 收藏

            精品国产婷婷久久久| 亚洲乱码中文字幕久久孕妇黑人| 一本一本久久a久久综合精品蜜桃| 欧美麻豆久久久久久中文| 国产精品免费久久久久影院| 日韩电影久久久被窝网| 久久妇女高潮几次MBA| 曰曰摸天天摸人人看久久久| 亚洲日韩欧美一区久久久久我| 国产午夜福利精品久久2021| 久久黄视频| 久久精品男人影院| 久久久久久久久66精品片| 国产精品久久久久久福利漫画| 亚洲中文字幕无码一久久区| 国产精品va久久久久久久| 狠狠色婷婷久久综合频道日韩| 国产精品久久精品| 99久久这里只精品国产免费| 国产精品久久久久久| 日本久久久久亚洲中字幕| 伊人久久综在合线亚洲2019| 久久久高清免费视频| 日韩AV毛片精品久久久| 精品水蜜桃久久久久久久| 久久精品九九亚洲精品天堂| 久久综合亚洲欧美成人| 久久久久久综合网天天| 狠狠色丁香婷婷久久综合| 久久青青国产| 人妻无码久久精品| 久久久黄片| 亚洲国产香蕉人人爽成AV片久久| 久久综合九色欧美综合狠狠 | 精品久久久久久无码人妻热| 老男人久久青草av高清| 中文字幕精品久久| 国内精品久久久久影院亚洲 | 伊人丁香狠狠色综合久久| 麻豆成人久久精品二区三区免费 | 伊人色综合久久天天网|