• <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語(yǔ)言文件追加參數(shù)操作

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

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

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

            c語(yǔ)言中swap問(wèn)題小結(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í)際傳過(guò)去是拷貝的一份,沒(méi)改變主函數(shù)中x,y

                swap2(&x,&y);

                printf("swap2: x:%d,y:%d\n",x,y);//不能交換,函數(shù)中只是地址交換了下,地址指向的內(nèi)容沒(mé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 一葉草 閱讀(1977) | 評(píng)論 (0)編輯 收藏

            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è)沒(méi)有學(xué)習(xí)過(guò) Makefile 書(shū)寫(xiě)規(guī)則的人,也可以為自己的 C/C++ 程序快速建立一個(gè)可工作的 Makefile.

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

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

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

              在程序目錄中創(chuàng)建一個(gè)名為 Makefile 的文本文件,將后面列出的 Makefile 的內(nèi)容復(fù)制到這個(gè)文件中。(注意:在復(fù)制的過(guò)程中,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)來(lái)確定。由于頭文件是通過(guò)包含來(lái)使用的,所以在這里說(shuō)的源程序不應(yīng)包含頭文件。

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

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

              雖然并不常用,但是 C 程序也可以被作為 C++ 程序編譯。這可以通過(guò)在 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 中提供的程序文件類(lèi)型(擴(kuò)展名)自動(dòng)判斷進(jìn)行的,不需要用戶(hù)干預(yù)。

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

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

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

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

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

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

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

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

              注意:在對(duì)程序編譯之后,會(huì)產(chǎn)生和源程序文件一一對(duì)應(yīng)的 .d 文件。這是表示依賴(lài)關(guān)系的文件,通過(guò)它們 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)文件和依賴(lài)文件。

              [*]make cleanall刪除目標(biāo)文件、依賴(lài)文件以及可執(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è)例子來(lái)具體說(shuō)明上面 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 兩種類(lèi)型 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 沒(méi)有過(guò)多的要求,LDFLAGS 和 CPPFLAGS 選項(xiàng)也無(wú)需設(shè)置。

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

              如果修改了源程序的話,可以看到只有和修改有關(guān)的源文件被編譯。也可以再為程序添加新的源文件,只要它們的擴(kuò)展名是已經(jīng)在 Makefile 中設(shè)置過(guò)的,那么就沒(méi)有必要修改 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 一種類(lèi)型 CFLAGS    := `pkg-config ——cflags gtk+-2.0`  # CFLAGS LDFLAGS   := `pkg-config ——libs gtk+-2.0`    # LDFLAGS

              這是一個(gè) C 程序,所以 CXXFLAGS 沒(méi)有必要設(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 一葉草 閱讀(533) | 評(píng)論 (0)編輯 收藏

            C++多繼承中的二義性

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

                多繼承下派生類(lèi)的定義格式如下:

                class <派生類(lèi)名>:<繼承方式1><基類(lèi)名1>,<繼承方式2><基類(lèi)名2>,…

                {

                <派生類(lèi)類(lèi)體>

                };

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

                class A

                {

                …

                };

                class B

                {

                …

                };

                class C : public A, public B

                {

                …

                };

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

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

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

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

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

                {

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

                }

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

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

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

                下面通過(guò)一個(gè)例子來(lái)說(shuō)明派生類(lèi)構(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)算符::用于解決作用域沖突的問(wèn)題。在派生類(lèi)A中的print()函數(shù)的定義中,使用了B1::print;和B2::print();語(yǔ)句分別指明調(diào)用哪一個(gè)類(lèi)中的print()函數(shù),這種用法應(yīng)該學(xué)會(huì)。

                二義性問(wèn)題

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

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

                下面再舉一個(gè)簡(jiǎn)單的例子,對(duì)二義性問(wèn)題進(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è)類(lèi)C的對(duì)象c1:

                C c1;

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

                c1.f();

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

                解決的方法可用前面用過(guò)的成員名限定法來(lái)消除二義性,例如:

                c1.A::f();

                或者

                c1.B::f();

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

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

                viod C::h()

                {

                f();

                }

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

                void C::h()

                {

                A::f();

                }

                或者

                void C::h()

                {

                B::f();

                }

                或者

                void C::f()

                {

                A::f();

                B::f();

                }

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

                c1.g();

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

                當(dāng)一個(gè)派生類(lèi)從多個(gè)基類(lèi)派生類(lèi),而這些基類(lèi)又有一個(gè)共同的基類(lèi),則對(duì)該基類(lèi)中說(shuō)明的成員進(jìn)行訪問(wè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è)訪問(wèn)都有二義性:

                c1.a;

                c1.A::a;

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

                c1.B1::a;

                c1.B2::a;

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

                int C::f()

                {

                retrun B1::a + B2::a;

                }

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

                class A : public B, public B

                {

                …

                }

                這是錯(cuò)誤的。

            posted @ 2012-03-20 22:31 一葉草 閱讀(543) | 評(píng)論 (0)編輯 收藏

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

            呵呵,最近幾天我有個(gè)小發(fā)現(xiàn),那就是老白沒(méi)有來(lái)看過(guò)我的博客了,說(shuō)真的蠻希望他能來(lái)的,
            他不來(lái)有點(diǎn)讓我失望,畢竟我也關(guān)注他很長(zhǎng)一段時(shí)間了,當(dāng)然,不管他來(lái)不來(lái),我自己的工作還是得繼續(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)過(guò)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ù)比較一遍,稱(chēng)為一輪比較。如果進(jìn)行一輪比較無(wú)交換,本文首發(fā)中國(guó)自學(xué)編程網(wǎng)排序完成。
                有無(wú)交換用一標(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無(wú)交換*/
                float t;/*中間變量,用于兩個(gè)數(shù)的交換*/
                do
                {flag=O;/*先假定無(wú)交換,已排好序*/
                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)讀者作類(lèi)似考慮。呵呵,差不多了,如果有不當(dāng)之處,請(qǐng)朋友們指正啊---
            posted @ 2012-03-18 13:31 一葉草 閱讀(687) | 評(píng)論 (0)編輯 收藏

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

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

                含有位段的結(jié)構(gòu)體類(lèi)型稱(chēng)為位段結(jié)構(gòu)。

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

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

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

                例如:

                struct bytedata

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

                unsigned:6;  /*無(wú)名位段,占6位,但不能訪問(wèn)*/

                unsigned:0;     /*無(wú)名位段,占0位,表下一位段從下一字邊界開(kāi)始*/

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

                int i;          /*成員i,從下一字邊界開(kā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)可以通過(guò)定義長(zhǎng)度為0的位段的方式使下一位段從下一存儲(chǔ)單元開(kāi)始。

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

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

                (5)位段無(wú)地址,不能對(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)位段的能力的,噢!你問(wèn)它到底是什么形式是吧?這個(gè)問(wèn)題呆會(huì)給你答案。讓我們先看看位段的作用:位段是在字段的聲明后面加一個(gè)冒號(hào)以及一個(gè)表示字段位長(zhǎng)的整數(shù)來(lái)實(shí)現(xiàn)的。這種用法又被就叫作“深入邏輯元件的編程”,如果你對(duì)系統(tǒng)編程感興趣,那么這篇文章你就不應(yīng)該錯(cuò)過(guò)!

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

                首先我要提醒大家注意幾點(diǎn):1、位段成員只有三種類(lèi)型:int ,unsigned int 和signed int這三種(當(dāng)然了,int型位段是不是可以取負(fù)數(shù)不是我說(shuō)了算的,因?yàn)檫@是和你的編譯器來(lái)決定的。位段,位段,它是用來(lái)表示字段位長(zhǎng)(bit)的,它只有整型值,不會(huì)有7.2這種float類(lèi)型的,如果你說(shuō)有,那你就等于承認(rèn)了有7.2個(gè)人這個(gè)概念,當(dāng)然也沒(méi)有char這個(gè)類(lèi)型的);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)是從左到右還是從右到左是由編譯器來(lái)決定的,但二者皆對(duì)。

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

                Struct WORD

                {

                unsigned int chara: 6:

                unsigned int font : 7;

                unsigned int maxsize : 19;

                };

                Struct WORD chone;

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

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

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

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

                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 來(lái)聲明位段后就省去了int ,這是可行的,詳見(jiàn)ANCI C標(biāo)準(zhǔn)。

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

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

                DISK->sector=fst_sector;

                DISK->track=fst_track;

                DISK->command=WRITE;

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

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

            posted @ 2012-03-11 17:35 一葉草 閱讀(543) | 評(píng)論 (0)編輯 收藏

            C++中使用Expat解析XML

            使用expat的原因很多,主要還是因?yàn)閑xpat更靈活。習(xí)慣了TinyXML,一開(kāi)始不太習(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開(kāi)始(含屬性)、element結(jié)束和文本內(nèi)容。回調(diào)函數(shù)的第一個(gè)參數(shù)是自定義的,通常用于存儲(chǔ) XML文檔的上下文信息,用XML_SetUserData可以設(shè)置這個(gè)參數(shù),下例中傳遞一個(gè)整數(shù)指針,以便在每次回調(diào)時(shí)能知道該元素是第幾層元素。

                該參數(shù)也可以是一個(gè)棧對(duì)象的地址,開(kāi)始一個(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.如何處理屬性

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

                好啦,先看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ù)本人還沒(méi)使用過(guò),涉及到更多的xml標(biāo)準(zhǔn)的知識(shí),如果需要,可以參考官方的手冊(cè)。

            posted @ 2012-02-26 13:31 一葉草 閱讀(2059) | 評(píng)論 (0)編輯 收藏

            c++ 處理Json

            一、摘要

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

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

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

            本文選擇一個(gè)第三方庫(kù) jsoncpp 來(lái)解析 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)品管理,此處沒(méi)必要將其編譯為動(dòng)態(tài)鏈接庫(kù)或者靜態(tài)導(dǎo)入庫(kù),所以我們選擇使用靜態(tài)鏈接庫(kù)[2]。

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

            四、jsoncpp 使用詳解

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

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

            1Value

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

            可如下是用 Json::Value 類(lèi):

            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),類(lèi)型為數(shù)組,對(duì)第一個(gè)元素賦值為字符串:"array_string"。

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

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

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

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

            2Writer

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

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

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

            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,下面我們來(lái)看看 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 是用于讀取的,說(shuō)的確切點(diǎn),是用于將字符串轉(zhuǎn)換為 Json::Value 對(duì)象的,下面我們來(lái)看個(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

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

            posted @ 2012-02-22 12:31 一葉草 閱讀(2199) | 評(píng)論 (0)編輯 收藏

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

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

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

              一、引入

              看下面的例子,這是我們?cè)诰帉?xiě)庫(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)地通過(guò)了,可是在運(yùn)行期他的程序崩潰終止。原因何在,是他觸犯了系統(tǒng)不可侵犯的條款:錯(cuò)誤地操作內(nèi)存。

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

              為了能徹底解決動(dòng)態(tài)內(nèi)存?zhèn)鬟f的問(wèn)題,我們先回顧一下內(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)存容量有限。

              從堆上分配,亦稱(chēng)動(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;開(kāi)辟空間或者使其指向?qū)ο螅?/p>

              pInt=new Int(3);或者int i=3;pint=&i;用指針(更確切地說(shuō)是操作內(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ù)的原因。

              三、問(wèn)題分析

              根據(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)闆](méi)有用free釋放內(nèi)存。

              四、問(wèn)題解決方案

              函數(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í)指針就可以了。原因通過(guò)函數(shù)傳遞規(guī)則可以很容易地分析出來(lái)。我們將& pMyReturn傳遞了進(jìn)去,就是將雙重指針的內(nèi)容傳遞到了函數(shù)中。函數(shù)過(guò)程利用改變指針的內(nèi)容,這樣pMyReturn很明顯指向了開(kāi)辟的內(nèi)存 .

              方法三:用函數(shù)返回值來(lái)傳遞動(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ù)寫(xiě)成這樣的話,你是不能返回什么動(dòng)態(tài)內(nèi)存的,因?yàn)閜指向的是字符串常量。內(nèi)存在位于靜態(tài)存儲(chǔ)區(qū)上分配,你無(wú)法改變。(你想要得到動(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),我們作為專(zhuān)業(yè)的軟件開(kāi)發(fā)人員。應(yīng)該深入理解并能靈活地掌握指針和內(nèi)存的操作。

            posted @ 2012-02-17 12:13 一葉草 閱讀(2529) | 評(píng)論 (11)編輯 收藏
            僅列出標(biāo)題
            共2頁(yè): 1 2 
            亚洲欧洲日产国码无码久久99| 国产精品九九久久免费视频 | 狠狠久久亚洲欧美专区| 国产偷久久久精品专区| 亚洲精品高清国产一线久久| 久久夜色撩人精品国产小说| 国产成人精品久久| 久久综合亚洲色HEZYO社区 | 精品伊人久久大线蕉色首页| 久久91亚洲人成电影网站| 久久人人爽人人爽人人片AV不| 激情综合色综合久久综合| 老男人久久青草av高清| 中文字幕日本人妻久久久免费 | 国内精品欧美久久精品| 亚洲欧美一级久久精品| 国产午夜精品久久久久免费视| 久久久精品波多野结衣| 国产成人久久精品一区二区三区| 久久精品aⅴ无码中文字字幕重口| 久久无码人妻精品一区二区三区| 久久伊人色| 久久亚洲国产精品成人AV秋霞| 亚洲中文久久精品无码| 麻豆精品久久精品色综合| 久久婷婷成人综合色综合| 久久97精品久久久久久久不卡| 久久er国产精品免费观看8| 日本欧美国产精品第一页久久| 国产免费久久精品丫丫| 久久成人小视频| 狠狠色婷婷综合天天久久丁香| 久久强奷乱码老熟女网站| 亚洲av日韩精品久久久久久a| 久久99国产精品久久久 | 成人国内精品久久久久影院VR| 午夜精品久久影院蜜桃| 99999久久久久久亚洲| 成人免费网站久久久| 伊人久久大香线蕉综合热线| 麻豆久久|