• <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>
            2012年6月3日

            C語(yǔ)言文件追加參數(shù)操作

            對(duì)文件進(jìn)行讀寫是常碰到操作,文件在進(jìn)行讀寫操作之前要先打開,使用完畢要關(guān)閉。所謂打開文件,實(shí)際上是建立文件的各種有關(guān)信息,并使文件指針指向該文件,以便進(jìn)行其它操作。通過c語(yǔ)言基礎(chǔ)培訓(xùn)可以基本掌握文件進(jìn)行讀寫操作。
               
                文件的打開(fopen函數(shù))
               
                fopen函數(shù)用來打開一個(gè)文件,其調(diào)用的一般形式為:文件指針名=fopen(文件名,使用文件方式); 其中,"文件指針名"必須是被說明為FILE 類型的指針變量;"文件名"是被打開文件的文件名;"使用文件方式"是指文件的類型和操作要求。 "文件名"是字符串常量或字符串?dāng)?shù)組。
               
                相關(guān)函數(shù) :open,fclose
               
                表頭文件 :#include<stdio.h>
               
                定義函數(shù) :FILE * fopen(const char * path,const char * mode);
               
                函數(shù)說明
               
                參數(shù)path字符串包含欲打開的文件路徑及文件名,參數(shù)mode字符串則代表著流形態(tài)。
               
                mode有下列幾種形態(tài)字符串:
               
                r 打開只讀文件,該文件必須存在。
               
                r+ 打開可讀寫的文件,該文件必須存在。
               
                w 打開只寫文件,若文件存在則文件長(zhǎng)度清為0,即該文件內(nèi)容會(huì)消失。若文件不存在則建立該文件。
               
                w+ 打開可讀寫文件,若文件存在則文件長(zhǎng)度清為零,即該文件內(nèi)容會(huì)消失。若文件不存在則建立該文件。
               
                a 以附加的方式打開只寫文件。若文件不存在,則會(huì)建立該文件,如果文件存在,寫入的數(shù)據(jù)會(huì)被加到文件尾,即文件原先的內(nèi)容會(huì)被保留。
               
                a+ 以附加方式打開可讀寫的文件。若文件不存在,則會(huì)建立該文件,如果文件存在,寫入的數(shù)據(jù)會(huì)被加到文件尾后,即文件原先的內(nèi)容會(huì)被保留。
               
                上述的形態(tài)字符串都可以再加一個(gè)b字符,如rb、w+b或ab+等組合,加入b 字符用來告訴函數(shù)庫(kù)打開的文件為二進(jìn)制文件,而非純文字文件。不過在POSIX系統(tǒng),包含Linux都會(huì)忽略該字符。由fopen()所建立的新文件會(huì)具有S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH(0666)權(quán)限,此文件權(quán)限也會(huì)參考umask 值。
               
                返回值
               
                文件順利打開后,指向該流的文件指針就會(huì)被返回。若果文件打開失敗則返回NULL,并把錯(cuò)誤代碼存在errno 中。
               
                附加說明
               
                一般而言,開文件后會(huì)作一些文件讀取或?qū)懭氲膭?dòng)作,若開文件失敗,接下來的讀寫動(dòng)作也無法順利進(jìn)行,所以在fopen()后請(qǐng)作錯(cuò)誤判斷及處理。
               
                范例
               
                #include<stdio.h>
               
                main()
               
                {
               
                FILE * fp;
               
                fp=fopen("noexist","a+");
               
                if(fp= =NULL) return;
               
                fclose(fp);
               
                }
            posted @ 2012-06-03 23:55 一葉草 閱讀(912) | 評(píng)論 (0)編輯 收藏
            2012年5月26日

            教你優(yōu)化C語(yǔ)言程序

            一般程序如果要進(jìn)行優(yōu)化,通常情況下是指優(yōu)化程序代碼或程序執(zhí)行速度。優(yōu)化代碼和優(yōu)化速度實(shí)際上是一個(gè)予盾的統(tǒng)一,一般是優(yōu)化了代碼的尺寸,就會(huì)帶來執(zhí)行時(shí)間的增加,如果優(yōu)化了程序的執(zhí)行速度,通常會(huì)帶來代碼增加的副作用,很難魚與熊掌兼得,只能在設(shè)計(jì)時(shí)掌握一個(gè)平衡點(diǎn)。
               
                一、程序結(jié)構(gòu)的優(yōu)化
               
                1、表達(dá)式
               
                對(duì)于一個(gè)表達(dá)式中各種運(yùn)算執(zhí)行的優(yōu)先順序不太明確或容易混淆的地方,應(yīng)當(dāng)采用圓括號(hào)明確指定它們的優(yōu)先順序。一個(gè)表達(dá)式通常不能寫得太復(fù)雜,如果表達(dá)式太復(fù)雜,時(shí)間久了以后,自己也不容易看得懂,不利于以后的維護(hù)。
               
                2、程序的書寫結(jié)構(gòu)
               
                雖然書寫格式并不會(huì)影響生成的代碼質(zhì)量,但是在實(shí)際編寫程序時(shí)還是應(yīng)該尊循一定的書寫規(guī)則,一個(gè)書寫清晰、明了的程序,有利于以后的維護(hù)。在書寫程序時(shí),特別是對(duì)于While、for、do…while、if…elst、switch…case等語(yǔ)句或這些語(yǔ)句嵌套組合時(shí),應(yīng)采用"縮格"的書寫形式,
               
                3、減少判斷語(yǔ)句
               
                能夠使用條件編譯(ifdef)的地方就使用條件編譯而不使用if語(yǔ)句,有利于減少編譯生成的代碼的長(zhǎng)度,能夠不用判斷語(yǔ)句則少用判斷用語(yǔ)句。
               
                4、標(biāo)識(shí)符
               
                程序中使用的用戶標(biāo)識(shí)符除要遵循標(biāo)識(shí)符的命名規(guī)則以外,一般不要用代數(shù)符號(hào)(如a、b、x1、y1)作為變量名,應(yīng)選取具有相關(guān)含義的英文單詞(或縮寫)或漢語(yǔ)拼音作為標(biāo)識(shí)符,以增加程序的可讀性,如:count、number1、red、work等。
               
                5、定義常數(shù)
               
                在程序化設(shè)計(jì)過程中,對(duì)于經(jīng)常使用的一些常數(shù),如果將它直接寫到程序中去,一旦常數(shù)的數(shù)值發(fā)生變化,就必須逐個(gè)找出程序中所有的常數(shù),并逐一進(jìn)行修改,這樣必然會(huì)降低程序的可維護(hù)性。因此,應(yīng)盡量當(dāng)采用預(yù)處理命令方式來定義常數(shù),而且還可以避免輸入錯(cuò)誤。
               
                二、代碼的優(yōu)化
               
                1、使用自加、自減指令
               
                通常使用自加、自減指令和復(fù)合賦值表達(dá)式(如a-=1及a+=1等)都能夠生成高質(zhì)量的程序代碼,編譯器通常都能夠生成inc和dec之類的指令,而使用a=a+1或a=a-1之類的指令,有很多C編譯器都會(huì)生成二到三個(gè)字節(jié)的指令。在AVR單片適用的ICCAVR、GCCAVR、IAR等C編譯器以上幾種書寫方式生成的代碼是一樣的,也能夠生成高質(zhì)量的inc和dec之類的的代碼。
               
                2、查表
               
                在程序中一般不進(jìn)行非常復(fù)雜的運(yùn)算,如浮點(diǎn)數(shù)的乘除及開方等,以及一些復(fù)雜的數(shù)學(xué)模型的插補(bǔ)運(yùn)算,對(duì)這些即消耗時(shí)間又消費(fèi)資源的運(yùn)算,應(yīng)盡量使用查表的方式,并且將數(shù)據(jù)表置于程序存儲(chǔ)區(qū)。如果直接生成所需的表比較困難,也盡量在啟動(dòng)時(shí)先計(jì)算,然后在數(shù)據(jù)存儲(chǔ)器中生成所需的表,后以在程序運(yùn)行直接查表就可以了,減少了程序執(zhí)行過程中重復(fù)計(jì)算的工作量。
               
                3、使用盡量小的數(shù)據(jù)類型
               
                能夠使用字符型(char)定義的變量,就不要使用整型(int)變量來定義;能夠使用整型變量定義的變量就不要用長(zhǎng)整型(long int),能不使用浮點(diǎn)型(float)變量就不要使用浮點(diǎn)型變量。當(dāng)然,在定義變量后不要超過變量的作用范圍,如果超過變量的范圍賦值,C編譯器并不報(bào)錯(cuò),但程序運(yùn)行結(jié)果卻錯(cuò)了,而且這樣的錯(cuò)誤很難發(fā)現(xiàn)。在ICCAVR中,可以在Options中設(shè)定使用printf參數(shù),盡量使用基本型參數(shù)(%c、%d、%x、%X、%u和%s格式說明符),少用長(zhǎng)整型參數(shù)(%ld、%lu、%lx和%lX格式說明符),至于浮點(diǎn)型的參數(shù)(%f)則盡量不要使用,其它C編譯器也一樣。在其它條件不變的情況下,使用%f參數(shù),會(huì)使生成的代碼的數(shù)量增加很多,執(zhí)行速度降低。
               
                4、選擇合適的算法和數(shù)據(jù)結(jié)構(gòu)
               
                應(yīng)該熟悉算法語(yǔ)言,知道各種算法的優(yōu)缺點(diǎn),具體資料請(qǐng)參見相應(yīng)的參考資料,有很多計(jì)算機(jī)書籍上都有介紹。將比較慢的順序查找法用較快的二分查找或亂序查找法代替,插入排序或冒泡排序法用快速排序、合并排序或根排序代替,都可以大大提高程序執(zhí)行的效率選擇一種合適的數(shù)據(jù)結(jié)構(gòu)也很重要,比如你在一堆隨機(jī)存放的數(shù)中使用了大量的插入和刪除指令,那使用鏈表要快得多。數(shù)組與指針語(yǔ)句具有十分密碼的關(guān)系,一般來說,指針比較靈活簡(jiǎn)潔,而數(shù)組則比較直觀,容易理解。對(duì)于大部分的編譯器,使用指針比使用數(shù)組生成的代碼更短,執(zhí)行效率更高。但是在Keil中則相反,使用數(shù)組比使用的指針生成的代碼更短。
            posted @ 2012-05-26 22:15 一葉草 閱讀(2218) | 評(píng)論 (1)編輯 收藏
            2012年4月8日

            c語(yǔ)言中swap問題小結(jié)

            #include<stdlib.h>

                #include<stdio.h>

                void swap1(int x,int y)

                {

                int temp;

                temp=x;

                x=y;

                y=temp;

                }

                void swap2(int *x,int *y)

                {

                int *temp;

                temp=x;

                x=y;

                y=temp;

                }

                void swap3(int *x,int *y)

                {

                int temp;

                temp=*x;

                *x=*y;

                *y=temp;

                }

                void swap4(int a[],int b[])

                {

                int temp;

                temp=a[0];

                a[0]=b[0];

                b[0]=temp;

                }

                void swap5(int a[],int b[])

                {

                int temp;

                temp=*a;

                *a=*b;

                *b=temp;

                }

                int main()

                {

                int x,y;

                x=4;

                y=3;

                swap1(x,y);

                printf("swap1: x:%d,y:%d\n",x,y);//形參傳值,不能交換,實(shí)際傳過去是拷貝的一份,沒改變主函數(shù)中x,y

                swap2(&x,&y);

                printf("swap2: x:%d,y:%d\n",x,y);//不能交換,函數(shù)中只是地址交換了下,地址指向的內(nèi)容沒有交換

                swap3(&x,&y);

                printf("swap3: x:%d,y:%d\n",x,y);//能交換,地址指向的內(nèi)容進(jìn)行了交換

                swap4(&x,&y);

                printf("swap4: x:%d,y:%d\n",x,y);//能交換,地址指向的內(nèi)容進(jìn)行交換

                swap5(&x,&y);

                printf("swap5: x:%d,y:%d\n",x,y);//能交換,地址指向的內(nèi)容進(jìn)行交換

                return 0;

                }

                swap1: x:4,y:3

                swap2: x:4,y:3

                swap3: x:3,y:4

                swap4: x:4,y:3

                swap5: x:3,y:4

            posted @ 2012-04-08 14:15 一葉草 閱讀(1967) | 評(píng)論 (0)編輯 收藏
            2012年3月28日

            C/C++ 通用 Makefile

            本文提供了一個(gè)用于對(duì) C/C++ 程序進(jìn)行編譯和連接以產(chǎn)生可執(zhí)行程序的通用 Makefile.

              在使用 Makefile 之前,只需對(duì)它進(jìn)行一些簡(jiǎn)單的設(shè)置即可;而且一經(jīng)設(shè)置,即使以后對(duì)源程序文件有所增減一般也不再需要改動(dòng) Makefile.因此,即便是一個(gè)沒有學(xué)習(xí)過 Makefile 書寫規(guī)則的人,也可以為自己的 C/C++ 程序快速建立一個(gè)可工作的 Makefile.

              這個(gè) Makefile 可以在 GNU Make 和 GCC 編譯器下正常工作。但是不能保證對(duì)于其它版本的 Make 和編譯器也能正常工作。

              如果你發(fā)現(xiàn)了本文中的錯(cuò)誤,或者對(duì)本文有什么感想或建議,可通過 whyglinux AT hotmail DOT com 郵箱和作者聯(lián)系。

              此 Makefile 的使用方法如下:[list=1][*]程序目錄的組織盡量將自己的源程序集中在一個(gè)目錄中,并且把 Makefile 和源程序放在一起,這樣用起來比較方便。當(dāng)然,也可以將源程序分類存放在不同的目錄中。

              在程序目錄中創(chuàng)建一個(gè)名為 Makefile 的文本文件,將后面列出的 Makefile 的內(nèi)容復(fù)制到這個(gè)文件中。(注意:在復(fù)制的過程中,Makfile 中各命令前面的 Tab 字符有可能被轉(zhuǎn)換成若干個(gè)空格。這種情況下需要把 Makefile 命令前面的這些空格替換為一個(gè) Tab.)

              將當(dāng)前工作目錄切換到 Makefile 所在的目錄。目前,這個(gè) Makefile 只支持在當(dāng)前目錄中的調(diào)用,不支持當(dāng)前目錄和 Makefile 所在的路徑不是同一目錄的情況。

              [*]指定可執(zhí)行文件程序編譯和連接成功后產(chǎn)生的可執(zhí)行文件在 Makefile 中的 PROGRAM 變量中設(shè)定。這一項(xiàng)不能為空。為自己程序的可執(zhí)行文件起一個(gè)有意義的名子吧。

              [*]指定源程序要編譯的源程序由其所在的路徑和文件的擴(kuò)展名兩項(xiàng)來確定。由于頭文件是通過包含來使用的,所以在這里說的源程序不應(yīng)包含頭文件。

              程序所在的路徑在 SRCDIRS 中設(shè)定。如果源程序分布在不同的目錄中,那么需要在 SRCDIRS 中一一指定,并且路徑名之間用空格分隔。

              在 SRCEXTS 中指定程序中使用的文件類型。C/C++ 程序的擴(kuò)展名一般有比較固定的幾種形式:。c、。C、。cc、。cpp、。CPP、。c++、。cp、或者。cxx(參見 man gcc)。擴(kuò)展名決定了程序是 C 還是 C++ 程序:。c 是 C 程序,其它擴(kuò)展名表示 C++ 程序。一般固定使用其中的一種擴(kuò)展名即可。但是也有可能需要使用多種擴(kuò)展名,這可以在 SOURCE_EXT 中一一指定,各個(gè)擴(kuò)展名之間用空格分隔。

              雖然并不常用,但是 C 程序也可以被作為 C++ 程序編譯。這可以通過在 Makefile 中設(shè)置 CC = $(CXX) 和 CFLAGS = $(CXXFLAGS) 兩項(xiàng)即可實(shí)現(xiàn)。

              這個(gè) Makefile 支持 C、C++ 以及 C/C++ 混合三種編譯方式:[list][*]如果只指定 .c 擴(kuò)展名,那么這是一個(gè) C 程序,用 $(CC) 表示的編譯命令進(jìn)行編譯和連接。

              [*]如果指定的是除 .c 之外的其它擴(kuò)展名(如 .cc、。cpp、。cxx 等),那么這是一個(gè) C++ 程序,用 $(CXX) 進(jìn)行編譯和連接。

              [*]如果既指定了 .c,又指定了其它 C++ 擴(kuò)展名,那么這是 C/C++ 混合程序,將用 $(CC) 編譯其中的 C 程序,用 $(CXX) 編譯其中的 C++ 程序,最后再用 $(CXX) 連接程序。

              [/list]這些工作都是 make 根據(jù)在 Makefile 中提供的程序文件類型(擴(kuò)展名)自動(dòng)判斷進(jìn)行的,不需要用戶干預(yù)。

              [*]指定編譯選項(xiàng)編譯選項(xiàng)由三部分組成:預(yù)處理選項(xiàng)、編譯選項(xiàng)以及連接選項(xiàng),分別由 CPPFLAGS、CFLAGS與CXXFLAGS、LDFLAGS 指定。

              CPPFLAGS 選項(xiàng)可參考 C 預(yù)處理命令 cpp 的說明,但是注意不能包含 -M 以及和 -M 有關(guān)的選項(xiàng)。如果是 C/C++ 混合編程,也可以在這里設(shè)置 C/C++ 的一些共同的編譯選項(xiàng)。

              CFLAGS 和 CXXFLAGS 兩個(gè)變量通常用來指定編譯選項(xiàng)。前者僅僅用于指定 C 程序的編譯選項(xiàng),后者僅僅用于指定 C++ 程序的編譯選項(xiàng)。其實(shí)也可以在兩個(gè)變量中指定一些預(yù)處理選項(xiàng)(即一些本來應(yīng)該放在 CPPFLAGS 中的選項(xiàng)),和 CPPFLAGS 并沒有明確的界限。

              連接選項(xiàng)在 LDFLAGS 中指定。如果只使用 C/C++ 標(biāo)準(zhǔn)庫(kù),一般沒有必要設(shè)置。如果使用了非標(biāo)準(zhǔn)庫(kù),應(yīng)該在這里指定連接需要的選項(xiàng),如庫(kù)所在的路徑、庫(kù)名以及其它聯(lián)接選項(xiàng)。

              現(xiàn)在的庫(kù)一般都提供了一個(gè)相應(yīng)的 .pc 文件來記錄使用庫(kù)所需要的預(yù)編譯選項(xiàng)、編譯選項(xiàng)和連接選項(xiàng)等信息,通過 pkg-config 可以動(dòng)態(tài)提取這些選項(xiàng)。與由用戶顯式指定各個(gè)選項(xiàng)相比,使用 pkg-config 來訪問庫(kù)提供的選項(xiàng)更方便、更具通用性。在后面可以看到一個(gè) GTK+ 程序的例子,其編譯和連接選項(xiàng)的指定就是用 pkg-config 實(shí)現(xiàn)的。

              [*]編譯和連接上面的各項(xiàng)設(shè)置好之后保存 Makefile 文件。執(zhí)行 make 命令,程序就開始編譯了。

              命令 make 會(huì)根據(jù) Makefile 中設(shè)置好的路徑和文件類型搜索源程序文件,然后根據(jù)文件的類型調(diào)用相應(yīng)的編譯命令、使用相應(yīng)的編譯選項(xiàng)對(duì)程序進(jìn)行編譯。

              編譯成功之后程序的連接會(huì)自動(dòng)進(jìn)行。如果沒有錯(cuò)誤的話最終會(huì)產(chǎn)生程序的可執(zhí)行文件。

              注意:在對(duì)程序編譯之后,會(huì)產(chǎn)生和源程序文件一一對(duì)應(yīng)的 .d 文件。這是表示依賴關(guān)系的文件,通過它們 make 決定在源程序文件變動(dòng)之后要進(jìn)行哪些更新。為每一個(gè)源程序文件建立相應(yīng)的 .d 文件這也是 GNU Make 推薦的方式。

              [*]Makefile 目標(biāo)(Targets)

              下面是關(guān)于這個(gè) Makefile 提供的目標(biāo)以及它所完成的功能:[list][*]make編譯和連接程序。相當(dāng)于 make all. [*]make objs僅僅編譯程序產(chǎn)生 .o 目標(biāo)文件,不進(jìn)行連接(一般很少單獨(dú)使用)。

              [*]make clean刪除編譯產(chǎn)生的目標(biāo)文件和依賴文件。

              [*]make cleanall刪除目標(biāo)文件、依賴文件以及可執(zhí)行文件。

              [*]make rebuild重新編譯和連接程序。相當(dāng)于 make clean && make all. [/list][/list]關(guān)于這個(gè) Makefile 的實(shí)現(xiàn)原理不準(zhǔn)備詳細(xì)解釋了。如果有興趣的話,可參考文末列出的“參考資料”。

              Makefile 的內(nèi)容如下:############################################################################### # # Generic Makefile for C/C++ Program # # Author: whyglinux (whyglinux AT hotmail DOT com) # Date:   2006/03/04 # Description: # The makefile searches in <SRCDIRS> directories for the source files # with extensions specified in <SOURCE_EXT>, then compiles the sources # and finally produces the <PROGRAM>, the executable file, by linking # the objectives. # Usage: #   $ make           compile and link the program. #   $ make objs      compile only (no linking. Rarely used)。 #   $ make clean     clean the objectives and dependencies. #   $ make cleanall  clean the objectives, dependencies and executable. #   $ make rebuild   rebuild the program. The same as make clean && make all. #============================================================================== ## Customizing Section: adjust the following if necessary. ##============================================================================= # The executable file name. # It must be specified. # PROGRAM   := a.out    # the executable name PROGRAM   := # The directories in which source files reside. # At least one path should be specified. # SRCDIRS   := .        # current directory SRCDIRS   := # The source file types (headers excluded)。 # At least one type should be specified. # The valid suffixes are among of .c, .C, .cc, .cpp, .CPP, .c++, .cp, or .cxx. # SRCEXTS   := .c      # C program # SRCEXTS   := .cpp    # C++ program # SRCEXTS   := .c .cpp # C/C++ program SRCEXTS   := # The flags used by the cpp (man cpp for more)。 # CPPFLAGS  := -Wall -Werror # show all warnings and take them as errors CPPFLAGS  := # The compiling flags used only for C. # If it is a C++ program, no need to set these flags. # If it is a C and C++ merging program, set these flags for the C parts. CFLAGS    := CFLAGS    += # The compiling flags used only for C++. # If it is a C program, no need to set these flags. # If it is a C and C++ merging program, set these flags for the C++ parts. CXXFLAGS  := CXXFLAGS  += # The library and the link options ( C and C++ common)。 LDFLAGS   := LDFLAGS   += ## Implict Section: change the following only when necessary. ##============================================================================= # The C program compiler. Uncomment it to specify yours explicitly. #CC      = gcc # The C++ program compiler. Uncomment it to specify yours explicitly. #CXX     = g++ # Uncomment the 2 lines to compile C programs as C++ ones. #CC      = $(CXX) #CFLAGS  = $(CXXFLAGS) # The command used to delete file. #RM        = rm -f ## Stable Section: usually no need to be changed. But you can add more. ##============================================================================= SHELL   = /bin/sh SOURCES = $(foreach d,$(SRCDIRS),$(wildcard $(addprefix $(d)/*,$(SRCEXTS)))) OBJS    = $(foreach x,$(SRCEXTS), \       $(patsubst %$(x),%.o,$(filter %$(x),$(SOURCES)))) DEPS    = $(patsubst %.o,%.d,$(OBJS)) .PHONY : all objs clean cleanall rebuild all : $(PROGRAM) # Rules for creating the dependency files (。d)。 #—— %.d : %.c @$(CC) -MM -MD $(CFLAGS) $< %.d : %.C @$(CC) -MM -MD $(CXXFLAGS) $< %.d : %.cc @$(CC) -MM -MD $(CXXFLAGS) $< %.d : %.cpp @$(CC) -MM -MD $(CXXFLAGS) $< %.d : %.CPP @$(CC) -MM -MD $(CXXFLAGS) $< %.d : %.c++ @$(CC) -MM -MD $(CXXFLAGS) $< %.d : %.cp @$(CC) -MM -MD $(CXXFLAGS) $< %.d : %.cxx @$(CC) -MM -MD $(CXXFLAGS) $< # Rules for producing the objects. #—— objs : $(OBJS) %.o : %.c $(CC) -c $(CPPFLAGS) $(CFLAGS) $< %.o : %.C $(CXX) -c $(CPPFLAGS) $(CXXFLAGS) $< %.o : %.cc $(CXX) -c $(CPPFLAGS) $(CXXFLAGS) $< %.o : %.cpp $(CXX) -c $(CPPFLAGS) $(CXXFLAGS) $< %.o : %.CPP $(CXX) -c $(CPPFLAGS) $(CXXFLAGS) $< %.o : %.c++ $(CXX -c $(CPPFLAGS) $(CXXFLAGS) $< %.o : %.cp $(CXX) -c $(CPPFLAGS) $(CXXFLAGS) $< %.o : %.cxx $(CXX) -c $(CPPFLAGS) $(CXXFLAGS) $< # Rules for producing the executable. #—— $(PROGRAM) : $(OBJS) ifeq ($(strip $(SRCEXTS)), .c)  # C file $(CC) -o $(PROGRAM) $(OBJS) $(LDFLAGS) else                            # C++ file $(CXX) -o $(PROGRAM) $(OBJS) $(LDFLAGS) endif -include $(DEPS) rebuild: clean all clean : @$(RM) *.o *.d cleanall: clean @$(RM) $(PROGRAM) $(PROGRAM)。exe ### End of the Makefile ##  Suggestions are welcome  ## All rights reserved ### ###############################################################################

              下面提供兩個(gè)例子來具體說明上面 Makefile 的用法。

              [color=darkred]例一 Hello World 程序[/color]

              這個(gè)程序的功能是輸出 Hello, world! 這樣一行文字。由 hello.h、hello.c、main.cxx 三個(gè)文件組成。前兩個(gè)文件是 C 程序,后一個(gè)是 C++ 程序,因此這是一個(gè) C 和 C++ 混編程序。

              /* File name: hello.h  * C header file  */ #ifndef HELLO_H #define HELLO_H #ifdef __cplusplus extern "C" { #endif   void print_hello(); #ifdef __cplusplus } #endif #endif

              /* File name: hello.c  * C source file.  */ #include "hello.h" #include <stdio.h> void print_hello() {   puts( "Hello, world!" ); }

              /* File name: main.cxx  * C++ source file.  */ #include "hello.h" int main() {   print_hello();   return 0; }

              建立一個(gè)新的目錄,然后把這三個(gè)文件拷貝到目錄中,也把 Makefile 文件拷貝到目錄中。之后,對(duì) Makefile 的相關(guān)項(xiàng)目進(jìn)行如下設(shè)置:PROGRAM   := hello      # 設(shè)置運(yùn)行程序名 SRCDIRS   := .          # 源程序位于當(dāng)前目錄下 SRCEXTS   := .c .cxx    # 源程序文件有 .c 和 .cxx 兩種類型 CFLAGS    := -g         # 為 C 目標(biāo)程序包含 GDB 可用的調(diào)試信息 CXXFLAGS  := -g         # 為 C++ 目標(biāo)程序包含 GDB 可用的調(diào)試信息

              由于這個(gè)簡(jiǎn)單的程序只使用了 C 標(biāo)準(zhǔn)庫(kù)的函數(shù)(puts),所以對(duì)于 CFLAGS 和 CXXFLAGS 沒有過多的要求,LDFLAGS 和 CPPFLAGS 選項(xiàng)也無需設(shè)置。

              經(jīng)過上面的設(shè)置之后,執(zhí)行 make 命令就可以編譯程序了。如果沒有錯(cuò)誤出現(xiàn)的話,。/hello  就可以運(yùn)行程序了。

              如果修改了源程序的話,可以看到只有和修改有關(guān)的源文件被編譯。也可以再為程序添加新的源文件,只要它們的擴(kuò)展名是已經(jīng)在 Makefile 中設(shè)置過的,那么就沒有必要修改 Makefile.

              [color=darkred]例二 GTK+ 版 Hello World 程序[/color]

              這個(gè) GTK+ 2.0 版的 Hello World 程序可以從下面的網(wǎng)址上得到:http://www.gtk.org/tutorial/c58.html#SEC-HELLOWORLD.當(dāng)然,要編譯 GTK+ 程序,還需要你的系統(tǒng)上已經(jīng)安裝好了 GTK+.

              跟第一個(gè)例子一樣,單獨(dú)創(chuàng)建一個(gè)新的目錄,把上面網(wǎng)頁(yè)中提供的程序保存為 main.c 文件。對(duì) Makefile 做如下設(shè)置:PROGRAM   := hello      # 設(shè)置運(yùn)行程序名 SRCDIRS   := .          # 源程序位于當(dāng)前目錄下 SRCEXTS   := .c         # 源程序文件只有 .c 一種類型 CFLAGS    := `pkg-config ——cflags gtk+-2.0`  # CFLAGS LDFLAGS   := `pkg-config ——libs gtk+-2.0`    # LDFLAGS

              這是一個(gè) C 程序,所以 CXXFLAGS 沒有必要設(shè)置——即使被設(shè)置了也不會(huì)被使用。

              編譯和連接 GTK+ 庫(kù)所需要的 CFLAGS 和 LDFLAGS 由 pkg-config 程序自動(dòng)產(chǎn)生。

              現(xiàn)在就可以運(yùn)行 make 命令編譯、。/hello 執(zhí)行這個(gè) GTK+ 程序了。

            posted @ 2012-03-28 19:17 一葉草 閱讀(521) | 評(píng)論 (0)編輯 收藏
            2012年3月20日

            C++多繼承中的二義性

            多繼承可以看作是單繼承的擴(kuò)展。所謂多繼承是指派生類具有多個(gè)基類,派生類與每個(gè)基類之間的關(guān)系仍可看作是一個(gè)單繼承。

                多繼承下派生類的定義格式如下:

                class <派生類名>:<繼承方式1><基類名1>,<繼承方式2><基類名2>,…

                {

                <派生類類體>

                };

                其中,<繼承方式1>,<繼承方式2>,…是三種繼承方式:public、private、protected之一。例如:

                class A

                {

                …

                };

                class B

                {

                …

                };

                class C : public A, public B

                {

                …

                };

                其中,派生類C具有兩個(gè)基類(類A和類B),因此,類C是多繼承的。按照繼承的規(guī)定,派生類C的成員包含了基類A, B中成員以及該類本身的成員。

                多繼承的構(gòu)造函數(shù)

                在多繼承的情況下,派生類的構(gòu)造函數(shù)格式如下:

                <派生類名>(<總參數(shù)表>):<基類名1>(<參數(shù)表1>),<基類名2>(<參數(shù)表2>),…

                <子對(duì)象名>(<參數(shù)表n+1>),…

                {

                <派生類構(gòu)造函數(shù)體>

                }

                其中,<總參數(shù)表>中各個(gè)參數(shù)包含了其后的各個(gè)分參數(shù)表。

                多繼承下派生類的構(gòu)造函數(shù)與單繼承下派生類構(gòu)造函數(shù)相似,它必須同時(shí)負(fù)責(zé)該派生類所有基類構(gòu)造函數(shù)的調(diào)用。同時(shí),派生類的參數(shù)個(gè)數(shù)必須包含完成所有基類初始化所需的參數(shù)個(gè)數(shù)。

                派生類構(gòu)造函數(shù)執(zhí)行順序是先執(zhí)行所屬基類的構(gòu)造函數(shù),再執(zhí)行派生類本身構(gòu)造函數(shù),處于同一層次的各基類構(gòu)造函數(shù)的執(zhí)行順序取決于定義派生類時(shí)所指定的各基類順序,與派生類構(gòu)造函數(shù)中所定義的成員初始化列表的各項(xiàng)順序無關(guān)。也就是說,執(zhí)行基類構(gòu)造函數(shù)的順序取決于定義派生類時(shí)基類的順序。可見,派生類構(gòu)造函數(shù)的成員初始化列表中各項(xiàng)順序可以任意地排列。

                下面通過一個(gè)例子來說明派生類構(gòu)造函數(shù)的構(gòu)成及其執(zhí)行順序。

                #include <iostream.h>

                class B1

                {

                public:

                B1(int i)

                {

                b1 = i;

                cout《"構(gòu)造函數(shù) B1."《i《 endl;

                }

                void print()

                {

                cout《"B1.print()"《b1《endl;

                }

                private:

                int b1;

                };

                class B2

                {

                public:

                B2(int i)

                {

                b2 = i;

                cout《"構(gòu)造函數(shù) B2."《i《 endl;

                }

                void print()

                {

                cout《"B2.print()"《b2《endl;

                }

                private:

                int b2;

                };

                class B3

                {

                public:

                B3(int i)

                {

                b3 = i;

                cout《"構(gòu)造函數(shù) B3."《i《endl;

                }

                int getb3()

                {

                return b3;

                }

                private:

                int b3;

                };

                class A : public B2, public B1

                {

                public:

                A(int i, int j, int k, int l):B1(i), B2(j), bb(k)

                {

                a = l;

                cout《"構(gòu)造函數(shù) A."《a《endl;

                }

                void print()

                {

                B1::print();

                B2::print();

                cout《"A.print()"《a《","《bb.getb3()《endl;

                }

                private:

                int a;

                B3 bb;

                };

                void main()

                {

                A aa(1, 2, 3, 4);

                aa.print();

                }


            該程序的輸出結(jié)果為:

                構(gòu)造函數(shù) B2.2

                構(gòu)造函數(shù) B1.1

                構(gòu)造函數(shù) B3.3

                構(gòu)造函數(shù) A.4

                B1.print()。1

                B2.print()2

                A.print()4, 3

                在該程序中,作用域運(yùn)算符::用于解決作用域沖突的問題。在派生類A中的print()函數(shù)的定義中,使用了B1::print;和B2::print();語(yǔ)句分別指明調(diào)用哪一個(gè)類中的print()函數(shù),這種用法應(yīng)該學(xué)會(huì)。

                二義性問題

                一般說來,在派生類中對(duì)基類成員的訪問應(yīng)該是唯一的,但是,由于多繼承情況下,可能造成對(duì)基類中某成員的訪問出現(xiàn)了不唯一的情況,則稱為對(duì)基類成員訪問的二義性問題。

                實(shí)際上,在上例已經(jīng)出現(xiàn)過這一問題,回憶一下上例中,派生類A的兩基類B1和B2中都有一個(gè)成員函數(shù)print()。如果在派生類中訪問 print()函數(shù),到底是哪一個(gè)基類的呢?于是出現(xiàn)了二義性。但是在上例中解決了這個(gè)問題,其辦法是通過作用域運(yùn)算符::進(jìn)行了限定。如果不加以限定,則會(huì)出現(xiàn)二義性問題。

                下面再舉一個(gè)簡(jiǎn)單的例子,對(duì)二義性問題進(jìn)行深入討論。例如:

                class A

                {

                public:

                void f();

                };

                class B

                {

                public:

                void f();

                void g();

                };

                class C : public A, public B

                {

                public:

                void g();

                void h();

                };

                如果定義一個(gè)類C的對(duì)象c1:

                C c1;

                則對(duì)函數(shù)f()的訪問

                c1.f();

                便具有二義性:是訪問類A中的f(),還是訪問類B中的f()呢?

                解決的方法可用前面用過的成員名限定法來消除二義性,例如:

                c1.A::f();

                或者

                c1.B::f();

                但是,最好的解決辦法是在類C中定義一個(gè)同名成員f(),類C中的f()再根據(jù)需要來決定調(diào)用A::f(),還是B::f(),還是兩者皆有,這樣,c1.f()將調(diào)用C::f()。

                同樣地,類C中成員函數(shù)調(diào)用f()也會(huì)出現(xiàn)二義性問題。例如:

                viod C::h()

                {

                f();

                }

                這里有二義性問題,該函數(shù)應(yīng)修改為:

                void C::h()

                {

                A::f();

                }

                或者

                void C::h()

                {

                B::f();

                }

                或者

                void C::f()

                {

                A::f();

                B::f();

                }

                另外,在前例中,類B中有一個(gè)成員函數(shù)g(),類C中也有一個(gè)成員函數(shù)g()。這時(shí),

                c1.g();

                不存在二義性,它是指C::g(),而不是指B::g()。因?yàn)檫@兩個(gè)g()函數(shù),一個(gè)出現(xiàn)在基類B,一個(gè)出現(xiàn)在派生類C,規(guī)定派生類的成員將支配基類中的同名成員。因此,上例中類C中的g()支配類B中的g(),不存在二義性,可選擇支配者的那個(gè)名字。

                當(dāng)一個(gè)派生類從多個(gè)基類派生類,而這些基類又有一個(gè)共同的基類,則對(duì)該基類中說明的成員進(jìn)行訪問時(shí),也可能會(huì)出現(xiàn)二義性。例如:

                class A

                {

                public:

                int a;

                };

                class B1 : public A

                {

                private:

                int b1;

                };

                class B2 : public A

                {

                private:

                int b2;

                };

                class C : public B1, public B2

                {

                public:

                int f();

                private:

                int c;

                };

                已知:C c1;

                下面的兩個(gè)訪問都有二義性:

                c1.a;

                c1.A::a;

                而下面的兩個(gè)訪問是正確的:

                c1.B1::a;

                c1.B2::a;

                類C的成員函數(shù)f()用如下定義可以消除二義性:

                int C::f()

                {

                retrun B1::a + B2::a;

                }

                由于二義性的原因,一個(gè)類不可以從同一個(gè)類中直接繼承一次以上,例如:

                class A : public B, public B

                {

                …

                }

                這是錯(cuò)誤的。

            posted @ 2012-03-20 22:31 一葉草 閱讀(533) | 評(píng)論 (0)編輯 收藏
            2012年3月18日

            將N個(gè)實(shí)數(shù)由大到小排序

            呵呵,最近幾天我有個(gè)小發(fā)現(xiàn),那就是老白沒有來看過我的博客了,說真的蠻希望他能來的,
            他不來有點(diǎn)讓我失望,畢竟我也關(guān)注他很長(zhǎng)一段時(shí)間了,當(dāng)然,不管他來不來,我自己的工作還是得繼續(xù)下去的嘛中,對(duì)不對(duì),這里我將簡(jiǎn)單對(duì)于將n個(gè)實(shí)數(shù)由大到小排序做個(gè)介紹吧。
                n個(gè)實(shí)數(shù)用數(shù)組a描述。
                本例提供用選擇排序方法與冒泡排序方法分別實(shí)現(xiàn)n個(gè)實(shí)數(shù)由大到小排序的函數(shù)。
                算法一:選擇排序。
                選擇排序需反復(fù)進(jìn)行求最大值與交換兩個(gè)數(shù)這兩種基本操作。
                對(duì)a[o]、a[1]、…、a[n一1]由大到小排序:先求所有數(shù)的最大值,然后將最大值與a[o]進(jìn)行交換;再求a[1]~a[n一1]這些數(shù)的最大值,然后將最大值與a[1]進(jìn)行交換;再求a[2]~a[n一1]這些數(shù)的最大值,然后將最大值與a[2]進(jìn)行交換……;最后求a[n一2]與a[n一1]這些數(shù)的最大值,然后將最大值與a[n一2]進(jìn)行交換。如此,經(jīng)過n一1輪處理完成排序,本文首發(fā)中國(guó)自學(xué)編程網(wǎng)。
                程序如下:
                void sortl(a,n)/*選擇排序函數(shù)*/
                float a[];
                int n:
                {int k,i,j;/*k最大值下標(biāo),i,j循環(huán)控制變量*/
                float t;/*中間變量,用于兩個(gè)數(shù)的交換*/
                for(i=0;i<n-1;i++)
                {k=i;/*求最大值下標(biāo)*/
                for(j=i+1}j<n;j++)
                if(a[j]>a[k])k=j
                t=a[i];a[i]一a[k];a[k]=t;/*進(jìn)行交換*/
                }
                }
                算法二:冒泡排序。
                冒泡排序需反復(fù)進(jìn)行相鄰兩個(gè)數(shù)的比較與交換兩個(gè)數(shù)這兩種基本操作。對(duì)相鄰的兩個(gè)數(shù)進(jìn)行比較時(shí),如果后面的數(shù)大于前面的數(shù),將這兩個(gè)數(shù)進(jìn)行交換,大的數(shù)往前冒。將所有相鄰的兩個(gè)安全閥數(shù)比較一遍,稱為一輪比較。如果進(jìn)行一輪比較無交換,本文首發(fā)中國(guó)自學(xué)編程網(wǎng)排序完成。
                有無交換用一標(biāo)志變量描述,一輪比較用for循環(huán)完成,整個(gè)排序利用標(biāo)志變量用條件循環(huán)控制。
                程序如下:
                void sort2(a,n)/*冒泡排序函數(shù)*/
                float a[];
                int n;
                {int i;/*一輪比較的循環(huán)控制變量*/
                int flag;/*標(biāo)志變量,為1有交換,為0無交換*/
                float t;/*中間變量,用于兩個(gè)數(shù)的交換*/
                do
                {flag=O;/*先假定無交換,已排好序*/
                for(i=O;i<n一2; i++)
                if(a[i+1]>a[i])
                {t=a[i];a[i]=a[i+1];a[i+1]=t;/*進(jìn)行交換*/
                flag=1;/*有交換,標(biāo)志變量的值改變?yōu)?*/
                }
                }while(flag==1);
                )
                由小到大排序請(qǐng)讀者作類似考慮。呵呵,差不多了,如果有不當(dāng)之處,請(qǐng)朋友們指正啊---
            posted @ 2012-03-18 13:31 一葉草 閱讀(676) | 評(píng)論 (0)編輯 收藏
            2012年3月11日

            c語(yǔ)言中位段的使用

            位段以位為單位定義結(jié)構(gòu)體(或共用體)中成員所占存儲(chǔ)空間的長(zhǎng)度。

                含有位段的結(jié)構(gòu)體類型稱為位段結(jié)構(gòu)。

                位段結(jié)構(gòu)也是一種結(jié)構(gòu)體類型,只不過其中含有以位為單位定義存儲(chǔ)長(zhǎng)度的整數(shù)類型位段成員。采用位段結(jié)構(gòu)既節(jié)省存儲(chǔ)空間,又可方便操作。

                位段結(jié)構(gòu)中位段的定義格式為:

                unsigned <成員名>:<二進(jìn)制位數(shù)>

                例如:

                struct bytedata

                {unsigned a:2;   /*位段a,占2位*/

                unsigned:6;  /*無名位段,占6位,但不能訪問*/

                unsigned:0;     /*無名位段,占0位,表下一位段從下一字邊界開始*/

                unsigned b:10;  /*位段b,占10位*/

                int i;          /*成員i,從下一字邊界開始*/

                }data;

                位段數(shù)據(jù)的引用:

                同結(jié)構(gòu)體成員中的數(shù)據(jù)引用一樣,但應(yīng)注意位段的最大取值范圍不要超出二進(jìn)制位數(shù)定的范圍,否則超出部分會(huì)丟棄。

                例如:data.a=2;   但  data.a=10;就超出范圍(a占2位,最大3)

                關(guān)于位段數(shù)據(jù),注意以下幾點(diǎn):

                (1)一個(gè)位段必須存儲(chǔ)在同一存儲(chǔ)單元(即字)之中,不能跨兩個(gè)單元。如果其單元空間不夠,則剩余空間不用,從下一個(gè)單元起存放該位段。

                (2)可以通過定義長(zhǎng)度為0的位段的方式使下一位段從下一存儲(chǔ)單元開始。

                (3)可以定義無名位段。

                (4)位段的長(zhǎng)度不能大于存儲(chǔ)單元的長(zhǎng)度。

                (5)位段無地址,不能對(duì)位段進(jìn)行取地址運(yùn)算。

                (6)位段可以以%d,%o,%x格式輸出。

                (7)位段若出現(xiàn)在表達(dá)式中,將被系統(tǒng)自動(dòng)轉(zhuǎn)換成整數(shù)。

                -------------------------------------------------------

                C語(yǔ)言中用結(jié)構(gòu)實(shí)現(xiàn)位段--個(gè)人心血!值得一看哦!C語(yǔ)言中的結(jié)構(gòu)是有實(shí)現(xiàn)位段的能力的,噢!你問它到底是什么形式是吧?這個(gè)問題呆會(huì)給你答案。讓我們先看看位段的作用:位段是在字段的聲明后面加一個(gè)冒號(hào)以及一個(gè)表示字段位長(zhǎng)的整數(shù)來實(shí)現(xiàn)的。這種用法又被就叫作“深入邏輯元件的編程”,如果你對(duì)系統(tǒng)編程感興趣,那么這篇文章你就不應(yīng)該錯(cuò)過!

                我把使用位段的幾個(gè)理由告訴大家:1、它能把長(zhǎng)度為奇數(shù)的數(shù)據(jù)包裝在一起,從而節(jié)省存儲(chǔ)的空間;2、它可以很方便地訪問一個(gè)整型值的部分內(nèi)容。

                首先我要提醒大家注意幾點(diǎn):1、位段成員只有三種類型:int ,unsigned int 和signed int這三種(當(dāng)然了,int型位段是不是可以取負(fù)數(shù)不是我說了算的,因?yàn)檫@是和你的編譯器來決定的。位段,位段,它是用來表示字段位長(zhǎng)(bit)的,它只有整型值,不會(huì)有7.2這種float類型的,如果你說有,那你就等于承認(rèn)了有7.2個(gè)人這個(gè)概念,當(dāng)然也沒有char這個(gè)類型的);2、成員名后面的一個(gè)冒號(hào)和一個(gè)整數(shù),這個(gè)整數(shù)指定該位段的位長(zhǎng)(bit);3、許多編譯器把位段成員的字長(zhǎng)限制在一個(gè)int的長(zhǎng)度范圍之內(nèi);4、位段成員在內(nèi)存的實(shí)現(xiàn)是從左到右還是從右到左是由編譯器來決定的,但二者皆對(duì)。

                下面我們就來看看,它到底是什么東西(我先假定大家的機(jī)器字長(zhǎng)為32位):

                Struct WORD

                {

                unsigned int chara: 6:

                unsigned int font : 7;

                unsigned int maxsize : 19;

                };

                Struct WORD chone;

                這一段是從我編寫的一個(gè)文字格式化軟件摘下來的,它最多可以容納64(既我說的unsigned int chara :6; 它總共是6位)個(gè)不同的字符值,可以處理128(既unsigned int font : 7 ;既2的7次方)種不同的字體,和2的19次方的單位長(zhǎng)度的字。大家都可以看到maxsize是19位,它是無法被一個(gè)short int 類型的值所容納的,我們又可以看到其余的成員的長(zhǎng)度比char還小,這就讓我們想起讓他們共享32位機(jī)器字長(zhǎng),這就避免用一個(gè)32位的整數(shù)來表示maxsize的位段。怎么樣?還要注意的是剛才的那一段代碼在16位字長(zhǎng)的機(jī)器上是無法實(shí)現(xiàn)的,為什么?提醒你一下,看看上面提醒的第3點(diǎn),你會(huì)明白的!

                你是不是發(fā)現(xiàn)這個(gè)東西沒有用啊?如果你點(diǎn)頭了,那你就錯(cuò)了!這么偉大的創(chuàng)造怎么會(huì)沒有用呢(你對(duì)系統(tǒng)編程不感興趣,相信你會(huì)改變這么一個(gè)觀點(diǎn)的)?磁盤控制器大家應(yīng)該知道吧?軟驅(qū)與它的通信我們來看看是怎么實(shí)現(xiàn)的下面是一個(gè)磁盤控制器的寄存器:

                │←5→│←5→│←9→│←8→│←1→│←1→∣←1→∣←1→∣←1→∣

                上面位段從左到右依次代表的含義為:5位的命令,5位的扇區(qū),9位的磁道,8位的錯(cuò)誤代碼,1位的HEAD LOADED,1位的寫保護(hù),1位的DISK SPINNING,1位的錯(cuò)誤判斷符,還有1位的READY位。它要怎么來實(shí)現(xiàn)呢?你先自己寫寫看:

                struct DISK_FORMAT

                {

                unsigned int command : 5;

                unsigned sector : 5;

                unsigned track : 9 ;

                unsigned err_code : 8;

                unsigned ishead_loaded : 1;

                unsigned iswrit_protect : 1;

                unsigned isdisk_spinning : 1;

                unsigned iserr_ocur : 1;

                undigned isready :1 ;

                };

                注:代碼中除了第一行使用了unsigned int 來聲明位段后就省去了int ,這是可行的,詳見ANCI C標(biāo)準(zhǔn)。

                如果我們要對(duì)044c18bfH的地址進(jìn)行訪問的話,那就這樣:

                #define DISK ((struct DISK_FORMAT *)0x044c18bf)

                DISK->sector=fst_sector;

                DISK->track=fst_track;

                DISK->command=WRITE;

                當(dāng)然那些都是要宏定義的哦!

                我們用位段來實(shí)現(xiàn)這一目的是很方便的,其實(shí)這也可以用移位或屏蔽來實(shí)現(xiàn),你嘗試過就知道哪個(gè)更方便了!

            posted @ 2012-03-11 17:35 一葉草 閱讀(535) | 評(píng)論 (0)編輯 收藏
            2012年2月26日

            C++中使用Expat解析XML

            使用expat的原因很多,主要還是因?yàn)閑xpat更靈活。習(xí)慣了TinyXML,一開始不太習(xí)慣expat,分析一下,其實(shí)很容易上手的。

                1.回調(diào)函數(shù)

                以下案例解析xml文件中的elment,attribute和text。expat使用回調(diào)方式返回xml數(shù)據(jù),解析器解析到一個(gè)element及其內(nèi)部屬性后,將調(diào)用事先設(shè)置好的函數(shù),同樣,當(dāng)element結(jié)束和text結(jié)束后,也會(huì)分別調(diào)用對(duì)應(yīng)的函數(shù)。

                2.如何處理數(shù)據(jù)之間的包含關(guān)系

                典型的方式是定義三個(gè)函數(shù)分別處理elment開始(含屬性)、element結(jié)束和文本內(nèi)容。回調(diào)函數(shù)的第一個(gè)參數(shù)是自定義的,通常用于存儲(chǔ) XML文檔的上下文信息,用XML_SetUserData可以設(shè)置這個(gè)參數(shù),下例中傳遞一個(gè)整數(shù)指針,以便在每次回調(diào)時(shí)能知道該元素是第幾層元素。

                該參數(shù)也可以是一個(gè)棧對(duì)象的地址,開始一個(gè)元素時(shí),將新元素對(duì)應(yīng)的數(shù)據(jù)壓入堆棧,處理下一級(jí)元素時(shí),新元素是棧頂元素在子元素,然后處理完了繼續(xù)把該元素壓入堆棧,繼續(xù)下一級(jí)新的子元素。當(dāng)元素結(jié)束后,需要出棧,以便解析下個(gè)兄弟元素程時(shí)能取到父節(jié)點(diǎn)。

                好啦,基本應(yīng)用還是很簡(jiǎn)單的,實(shí)際上Expat的API函數(shù)不多。

                3.如何處理屬性

                屬性通過ElementHandler回調(diào)函數(shù)傳入,這里有一個(gè)char** atts就是屬性,這是一個(gè)字符指針數(shù)組,如果有N個(gè)屬性,數(shù)組大小就是2*N+1,最后一個(gè)素組元素為空指針,奇數(shù)指針對(duì)應(yīng)屬性名稱,偶數(shù)指針對(duì)應(yīng)屬性值(字符串格式)。可以在一個(gè)循環(huán)中處理多個(gè)屬性,當(dāng)遇到空指針時(shí),表示沒有更多屬性了。

                好啦,先看sample吧:

                #include <stdio.h>

                #include "expat.h"

                #pragma warning(disable:4996)

                #define XML_FMT_INT_MOD "l"

                static void XMLCALL startElement(void *userData, const char *name, const char **atts)

                {

                int i;

                int *depthPtr = (int *)userData;

                for (i = 0; i < *depthPtr; i++)

                printf(" ");

                printf(name);

                *depthPtr += 1;

                for(i=0;atts[i]!=0;i+=2)

                {

                printf(" %s=%s",atts[i],atts[i+1]);

                }

                printf("\n");

                }

                static void XMLCALL endElement(void *userData, const char *name)

                {

                int *depthPtr = (int *)userData;

                *depthPtr -= 1;

                }

                int main(int argc, char *argv[])

                {

                char buf[BUFSIZ];  XML_Parser parser = XML_ParserCreate(NULL);

                int done;  int depth = 0;

                XML_SetUserData(parser, &depth);

                XML_SetElementHandler(parser, startElement, endElement);

                FILE* pFile= argc<2 ?stdin : fopen(argv[1],"rb");

                do

                {    int len = (int)fread(buf, 1, sizeof(buf), pFile);

                done = len < sizeof(buf);

                if (XML_Parse(parser, buf, len, done) == XML_STATUS_ERROR)

                {

                fprintf(stderr,"%s at line %" XML_FMT_INT_MOD "u\n",

                XML_ErrorString(XML_GetErrorCode(parser)),

                XML_GetCurrentLineNumber(parser));

                return 1;

                }

                }

                while (!done);

                XML_ParserFree(parser);

                fclose(pFile);

                return 0;

                }

                4.其他ElementHanlder

                expat還可以設(shè)置CData,Comment的handler,另外一些函數(shù)本人還沒使用過,涉及到更多的xml標(biāo)準(zhǔn)的知識(shí),如果需要,可以參考官方的手冊(cè)。

            posted @ 2012-02-26 13:31 一葉草 閱讀(2048) | 評(píng)論 (0)編輯 收藏
            2012年2月22日

            c++ 處理Json

            一、摘要

            JSON 的全稱為:JavaScript Object Notation,顧名思義,JSON 是用于標(biāo)記 Javascript 對(duì)象的,JSON 官方的解釋為:JSON 是一種輕量級(jí)的數(shù)據(jù)傳輸格式。

            本文并不詳細(xì)介紹 JSON 本身的細(xì)節(jié),旨在討論如何使用 C++ 語(yǔ)言來處理 JSON。關(guān)于 JSON 更具體的信息,可參見 JSON 官網(wǎng):http://www.json.org。

            二、本文選擇處理 JSON C++ 庫(kù)

            本文選擇一個(gè)第三方庫(kù) jsoncpp 來解析 JSON。jsoncpp 是比較出名的 C++ JSON 解析庫(kù)。在 JSON 官網(wǎng)也是首推的。

            下載地址為:http://sourceforge.net/projects/jsoncpp。本文使用的 jsoncpp 版本為:0.5.0。

            三、jsoncpp Windows 下的編譯

            要使用第三方源碼庫(kù),第一步少不了的就是編譯,將源碼文件編譯成我們方便使用的動(dòng)態(tài)鏈接庫(kù)、靜態(tài)鏈接庫(kù)或者靜態(tài)導(dǎo)入庫(kù)[1]。

            jsconcpp 進(jìn)行 JSON 解析的源碼文件分布在 include/json、src/lib_json 下。其實(shí) jsoncpp 源碼并不多,為了方便產(chǎn)品管理,此處沒必要將其編譯為動(dòng)態(tài)鏈接庫(kù)或者靜態(tài)導(dǎo)入庫(kù),所以我們選擇使用靜態(tài)鏈接庫(kù)[2]。

            jsoncpp 已經(jīng)處理的很完善了,所有編譯選項(xiàng)都已經(jīng)配置好,打開makefiles/vs71/jsoncpp.sln 便可以開始編譯(默認(rèn)是使用 VS2003 編譯器的,打開時(shí)直接按照 VS2005 提示轉(zhuǎn)換即可)。

            四、jsoncpp 使用詳解

            jsoncpp 主要包含三種類型的 class:Value、Reader、Writer。jsoncpp 中所有對(duì)象、類名都在 namespace Json 中,包含 json.h 即可。

            Json::Value 只能處理 ANSI 類型的字符串,如果 C++ 程序是用 Unicode 編碼的,最好加一個(gè) Adapt 類來適配。

            1Value

            Json::Value 是jsoncpp 中最基本、最重要的類,用于表示各種類型的對(duì)象,jsoncpp 支持的對(duì)象類型可見 Json::ValueType 枚舉值。

            可如下是用 Json::Value 類:

            Json::Value json_temp; // 臨時(shí)對(duì)象,供如下代碼使用

            json_temp["name"] = Json::Value("huchao");

            json_temp["age"] = Json::Value(26);

            Json::Value root; // 表示整個(gè) json 對(duì)象

            root["key_string"] = Json::Value("value_string"); // 新建一個(gè) Key(名為:key_string),賦予字符串值:"value_string"。

            root["key_number"] = Json::Value(12345); // 新建一個(gè) Key(名為:key_number),賦予數(shù)值:12345。

            root["key_boolean"] = Json::Value(false); // 新建一個(gè) Key(名為:key_boolean),賦予bool值:false。

            root["key_double"] = Json::Value(12.345); // 新建一個(gè) Key(名為:key_double),賦予 double 值:12.345。

            root["key_object"] = Json_temp; // 新建一個(gè) Key(名為:key_object),賦予 json::Value 對(duì)象值。

            root["key_array"].append("array_string"); // 新建一個(gè) Key(名為:key_array),類型為數(shù)組,對(duì)第一個(gè)元素賦值為字符串:"array_string"。

            root["key_array"].append(1234); // 為數(shù)組 key_array 賦值,對(duì)第二個(gè)元素賦值為:1234。

            Json::ValueType type = root.type(); // 獲得 root 的類型,此處為 objectValue 類型。

            注:跟C++ 不同,JavaScript 數(shù)組可以為任意類型的值,所以 jsoncpp 也可以。

            如上幾個(gè)用法已經(jīng)可以滿足絕大部分 json 應(yīng)用了,當(dāng)然 jsoncpp 還有一些其他同能,比如說設(shè)置注釋、比較 json 大小、交換 json 對(duì)象等,都很容易使用,大家自己嘗試吧。

            2Writer

            如上說了 Json::Value 的使用方式,現(xiàn)在到了該查看剛才賦值內(nèi)容的時(shí)候了,查看 json 內(nèi)容,使用 Writer 類即可。

            Jsoncpp 的 Json::Writer 類是一個(gè)純虛類,并不能直接使用。在此我們使用 Json::Writer 的子類:Json::FastWriter、Json::StyledWriter、Json::StyledStreamWriter。

            顧名思義,用 Json::FastWriter 來處理 json 應(yīng)該是最快的,下面我們來試試。

            Json::FastWriter fast_writer;

            std::cout << fast_writer.write(root) << std::endl;

            輸出結(jié)果為:

            {"key_array":["array_string",1234],"key_boolean":false,"key_double":12.3450,"key_number":12345,"key_object":{"age":26,"name":"huchao"},"key_string":"value_string"}

            再次顧名思義,用 Json::StyledWriter 是格式化后的 json,下面我們來看看 Json::StyledWriter 是怎樣格式化的。

            Json::StyledWriter styled_writer;

            std::cout << styled_writer.write(root) << std::endl;

            輸出結(jié)果為:

            {

            "key_array" : [ "array_string", 1234 ],

            "key_boolean" : false,

            "key_double" : 12.3450,

            "key_number" : 12345,

            "key_object" : {

            "age" : 26,

            "name" : "huchao"

            },

            "key_string" : "value_string"

            }

            3Reader

            Json::Reader 是用于讀取的,說的確切點(diǎn),是用于將字符串轉(zhuǎn)換為 Json::Value 對(duì)象的,下面我們來看個(gè)簡(jiǎn)單的例子。

            Json::Reader reader;

            Json::Value json_object;

            const char* json_document = "{\"age\" : 26,\"name\" : \"huchao\"}";

            if (!reader.parse(json_document, json_object))

            return 0;

            std::cout << json_object["name"] << std::endl;

            std::cout << json_object["age"] << std::endl;

            輸出結(jié)果為:

            "huchao"

            26

            可見,上述代碼已經(jīng)解析出了 json 字符串。

            posted @ 2012-02-22 12:31 一葉草 閱讀(2192) | 評(píng)論 (0)編輯 收藏
            2012年2月17日

            論C/C++函數(shù)間動(dòng)態(tài)內(nèi)存的傳遞

            當(dāng)你涉及到C/C++的核心編程的時(shí)候,你會(huì)無止境地與內(nèi)存管理打交道。這些往往會(huì)使人受盡折磨。所以如果你想深入C/C++編程,你必須靜下心來,好好苦一番。

              現(xiàn)在我們將討論C/C++里我認(rèn)為哪一本書都沒有完全說清楚,也是涉及概念細(xì)節(jié)最多,語(yǔ)言中最難的技術(shù)之一的動(dòng)態(tài)內(nèi)存的傳遞。并且在軟件開發(fā)中很多專業(yè)人員并不能寫出相關(guān)的合格的代碼。

              一、引入

              看下面的例子,這是我們?cè)诰帉憥?kù)函數(shù)或者項(xiàng)目?jī)?nèi)的共同函數(shù)經(jīng)常希望的。

              void MyFunc(char *pReturn, size_t size)

              {………

              pReturn = (char *)malloc(sizeof(char) * num);………

              }我們可以很明顯地看出代碼作者的意圖,他想在函數(shù)調(diào)用處聲明一個(gè)指針 char *pMyReturn=NULL;然后調(diào)用MyFunc處理并返回一段長(zhǎng)度為size的一段動(dòng)態(tài)內(nèi)存。

              那么作者能達(dá)到預(yù)期的效果嗎?

              那么我可以告訴作者,他的程序在編譯期很幸運(yùn)地通過了,可是在運(yùn)行期他的程序崩潰終止。原因何在,是他觸犯了系統(tǒng)不可侵犯的條款:錯(cuò)誤地操作內(nèi)存。

              二、內(nèi)存操作及問題相關(guān)知識(shí)點(diǎn)

              為了能徹底解決動(dòng)態(tài)內(nèi)存?zhèn)鬟f的問題,我們先回顧一下內(nèi)存管理的知識(shí)要點(diǎn)。

              (1)內(nèi)存分配方式有三種:

              從靜態(tài)存儲(chǔ)區(qū)域分配。內(nèi)存在程序編譯的時(shí)候就已經(jīng)分配好,這塊內(nèi)存在程序的整個(gè)運(yùn)行期間都存在。例如全局變量,static變量。

              在棧上創(chuàng)建。在執(zhí)行函數(shù)時(shí),函數(shù)內(nèi)局部變量的存儲(chǔ)單元都可以在棧上創(chuàng)建,函數(shù)執(zhí)行結(jié)束時(shí)這些存儲(chǔ)單元自動(dòng)被釋放。棧內(nèi)存分配運(yùn)算內(nèi)置于處理器的指令集中,效率很高,但是分配的內(nèi)存容量有限。

              從堆上分配,亦稱動(dòng)態(tài)內(nèi)存分配。程序在運(yùn)行的時(shí)候用malloc或new申請(qǐng)任意多少的內(nèi)存,程序員自己負(fù)責(zé)在何時(shí)用free或delete釋放內(nèi)存。動(dòng)態(tài)內(nèi)存的生存期由我們決定,使用非常靈活。

              (2)指針的操作流程

              申請(qǐng)并初始化或設(shè)置為空:

              int *pInt=NULL;開辟空間或者使其指向?qū)ο螅?/p>

              pInt=new Int(3);或者int i=3;pint=&i;用指針(更確切地說是操作內(nèi)存,在使用之前加if(pint!=NULL)或者assert(pInt!=NULL)后再使用,以防內(nèi)存申請(qǐng)失敗的情況下使用指針):

              if(p!=NULL) {use pint};釋放使用完的內(nèi)存

              free(pInt);置指針為空

              pInt=NULL;(避免野指針的出現(xiàn))

              (3)在函數(shù)的參數(shù)傳遞中,編譯器總是要為函數(shù)的每個(gè)參數(shù)制作臨時(shí)副本,如果參數(shù)為p的話,那么編譯器會(huì)產(chǎn)生p的副本_p,使_p=p; 如果函數(shù)體內(nèi)的程序修改了_p的內(nèi)容,就導(dǎo)致參數(shù)p的內(nèi)容作相應(yīng)的修改。這就是指針可以用作輸出參數(shù)的原因。

              三、問題分析

              根據(jù)上面的規(guī)則我們可以很容易分析例子中失敗的原因。

              void MyFunc(char *pReturn, size_t size)

              {………

              pReturn = (char *)malloc(sizeof(char) * num);………

              } void main(void){ char *pMyReturn=NULL;MyFunc(pMyReturn,10);}在MyFunc(char *pReturn, size_t size)中_pMyReturn真實(shí)地申請(qǐng)到了內(nèi)存, pMyReturn申請(qǐng)了新的內(nèi)存,只是把_pMyReturn 所指的內(nèi)存地址改變了,但是pMyReturn絲毫未變。所以函數(shù)MyFunc并不能輸出任何東西。事實(shí)上,每執(zhí)行一次MyFunc就會(huì)泄露一塊內(nèi)存,因?yàn)闆]有用free釋放內(nèi)存。

              四、問題解決方案

              函數(shù)間傳遞動(dòng)態(tài)數(shù)據(jù)我們可以有三種解決方法。

              方法一:如果我們是用C++編程,我們可以很方便地利用引用這個(gè)技術(shù)。我也極力推薦你用引用,因?yàn)樗鼤?huì)使你少犯一些錯(cuò)誤。以下是一個(gè)例子。

              void MyFunc(char* &pReturn,size_t size){ pReturn=(char*)malloc(size);memset(pReturn,0x00,size);if(size>=13)

              strcpy(pReturn,"Hello World!");}

              void main(){ char *pMyReturn=NULL;MyFunc(pMyReturn,15);if(pMyReturn!=NULL)

              { char *pTemp=pMyReturn;while(*pTemp!=''\0'')

              cout<<*pTemp++;pTemp=NULL;strcpy(pMyReturn,"AAAAAAAA");free(pMyReturn);pMyReturn=NULL;}方法二:利用二級(jí)指針

              void MyFunc (char ** pReturn, size_t size)

              { * pReturn = (char *)malloc(size);} void main(void)

              { char * pMyReturn = NULL;MyFunc (&pMyReturn, 100);// 注意參數(shù)是 & pMyReturn if(pMyReturn!=NULL){ strcpy(pMyReturn, "hello");cout<< pMyReturn << endl;free(pMyReturn);pMyReturn=NULL;}}為什么二級(jí)指針就可以了。原因通過函數(shù)傳遞規(guī)則可以很容易地分析出來。我們將& pMyReturn傳遞了進(jìn)去,就是將雙重指針的內(nèi)容傳遞到了函數(shù)中。函數(shù)過程利用改變指針的內(nèi)容,這樣pMyReturn很明顯指向了開辟的內(nèi)存 .

              方法三:用函數(shù)返回值來傳遞動(dòng)態(tài)內(nèi)存

              char * MyFunc (void)

              { char *p =new char[20];memset(p,0x00,sizeof(p));return p;} void main(void)

              { char *str = NULL;str = MyFunc();if(str!=NULL)

              { strcpy(str,"Hello,baby");cout<< str << endl;free(str);str=NULL;}請(qǐng)注意的是函數(shù)寫成這樣的話,你是不能返回什么動(dòng)態(tài)內(nèi)存的,因?yàn)閜指向的是字符串常量。內(nèi)存在位于靜態(tài)存儲(chǔ)區(qū)上分配,你無法改變。(你想要得到動(dòng)態(tài)內(nèi)存我們一定要看到malloc或者new)。

              char * MyFunc (void)

              { char *p =“Hello World”

              return p;}結(jié)束語(yǔ)

              操作內(nèi)存是C/C++一個(gè)難點(diǎn),我們作為專業(yè)的軟件開發(fā)人員。應(yīng)該深入理解并能靈活地掌握指針和內(nèi)存的操作。

            posted @ 2012-02-17 12:13 一葉草 閱讀(2516) | 評(píng)論 (11)編輯 收藏
            僅列出標(biāo)題  下一頁(yè)
            亚洲欧洲久久av| 久久国产高潮流白浆免费观看| 国产一区二区三区久久| 中文字幕成人精品久久不卡| 国内精品久久久久久麻豆| 亚洲成av人片不卡无码久久| 日韩欧美亚洲综合久久| 99久久99这里只有免费的精品| 久久99精品综合国产首页| 人妻丰满?V无码久久不卡| 亚洲精品乱码久久久久久按摩 | 一本伊大人香蕉久久网手机| 久久国产成人亚洲精品影院| 久久久久久国产精品无码下载| 色噜噜狠狠先锋影音久久| 精品久久久中文字幕人妻| 久久久国产精品网站| 亚洲精品蜜桃久久久久久| 久久一区二区三区免费| 国产成人综合久久综合| 2021国产精品久久精品| 精品久久国产一区二区三区香蕉 | 婷婷综合久久中文字幕蜜桃三电影 | 欧美伊人久久大香线蕉综合| 中文字幕亚洲综合久久2| 无码国内精品久久人妻蜜桃| 久久性生大片免费观看性| 99热成人精品免费久久| 久久久久久久99精品免费观看| 性高湖久久久久久久久| 伊人久久大香线蕉精品不卡 | 久久亚洲精品中文字幕| 久久综合色区| 色偷偷88欧美精品久久久| 久久精品国产亚洲一区二区| 久久Av无码精品人妻系列| 亚洲国产精品无码久久久蜜芽 | 久久久久久久综合综合狠狠| 国产高潮国产高潮久久久91 | 97久久国产露脸精品国产| 婷婷国产天堂久久综合五月|