• <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語言中可變參數函數的設計

            1,首先,怎么得到參數的值。對于一般的函數,我們可以通過參數對應在參數列表里的標識符來得到。但是參數可變函數那些可變的參數是沒有參數標識符的,它只有“…”,所以通過標識符來得到是不可能的,我們只有另辟途徑。

            我們知道函數調用時都會分配棧空間,而函數調用機制中的棧結構如下圖所示:

                                   |     ......     |

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

                                   |     參數2      |

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

                                   |     參數1      |

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

                                   |    返回地址    |

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

                                   |調用函數運行狀態|

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

            可見,參數是連續存儲在棧里面的,那么也就是說,我們只要得到可變參數的前一個參數的地址,就可以通過指針訪問到那些可變參數。但是怎么樣得到可變參數的前一個參數的地址呢?不知道你注意到沒有,參數可變函數在可變參數之前必有一個參數是固定的,并使用標識符,而且通常被聲明為char*類型,printf函數也不例外。這樣的話,我們就可以通過這個參數對應的標識符來得到地址,從而訪問其他參數變得可能。我們可以寫一個測試程序來試一下:

            #include <stdio.h>



            void va_test(char* fmt,...);//參數可變的函數聲明



            void main()

            {

                int a=1,c=55;

                   char b='b';

                va_test("",a,b,c);//用四個參數做測試

            }



            void va_test(char* fmt,...) //參數可變的函數定義,注意第一個參數為char* fmt

            {

               char *p=NULL;



                  p=(char *)&fmt;//注意不是指向fmt,而是指向&fmt,并且強制轉化為char *,以便一個一個字節訪問

                  for(int i = 0;i<16;i++)//16是通過計算的值(參數個數*4個字節),只是為了測試,暫且將就一下

                  {

                            printf("%.4d ",*p);//輸出p指針指向地址的值

                    p++;

                  }

            }



            編譯運行的結果為

            0056 0000 0066 0000 | 0001 0000 0000 0000 | 0098 0000 0000 0000 | 0055 0000 0000 0000



            由運行結果可見,通過這樣方式可以逐一獲得可變參數的值。

            至于為什么通常被聲明為char*類型,我們慢慢看來。

            2,怎樣確定參數類型和數量

            通過上述的方式,我們首先解決了取得可變參數值的問題,但是對于一個參數,值很重要,其類型同樣舉足輕重,而對于一個函數來講參數個數也非常重要,否則就會產生了一系列的麻煩來。通過訪問存儲參數的棧空間,我們并不能得到關于類型的任何信息和參數個數的任何信息。我想你應該想到了——使用char *參數。Printf函數就是這樣實現的,它把后面的可變參數類型都放到了char *指向的字符數組里,并通過%來標識以便與其它的字符相區別,從而確定了參數類型也確定了參數個數。其實,用何種方式來到達這樣的效果取決于函數的實現。比如說,定義一個函數,預知它的可變參數類型都是int,那么固定參數完全可以用int類型來替換char*類型,因為只要得到參數個數就可以了。

            3,言歸正傳

               我想到了這里,大概的輪廓已經呈現出來了。本來想就此作罷的(我的惰性使然),但是一想到如果不具實用性便可能是一堆廢物,枉費我打了這么些字,決定還是繼續下去。

               我是比較抵制用那些不明所以的宏定義的,所以在上面的闡述里一點都沒有涉及定義在<stdarg.h>的va(variable-argument)宏。事實上,當時讓我產生極大疑惑和好奇的正是這幾個宏定義。但是現在我們不得不要去和這些宏定義打打交道,畢竟我們在討生計的時候還得用上他們,這也是我曰之為“言歸正傳”的理由。

               好了,我們來看一下那些宏定義。

               打開<stdarg.h>文件,找一下va_*的宏定義,發現不單單只有一組,但是在各組定義前都會有宏編譯。宏編譯指示的是不同硬件平臺和編譯器下用怎樣的va宏定義。比較一下,不同之處主要在偏移量的計算上。我們還是拿個典型又熟悉的——X86的相關宏定義:

            1)typedef char * va_list;

            2)#define _INTSIZEOF(n)   ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )



            3)#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )

            4)#define va_arg(ap,t)    ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )

            5)#define va_end(ap)      ( ap = (va_list)0 )



            我們逐一看來:

            第一個我想不必說了,類型定義罷了。第二個是頗有些來頭的,我們也不得不搞懂它,因為后面的兩個關鍵的宏定義都用到了。不知道你夠不夠細心,有沒有發現在上面的測試程序中,第二個可變參數明明是char類型,可是在輸出結果中占了4個byte。難道所有的參數都會占4個byte的空間?那如果是double類型的參數,且不是會丟失數據!如果你不嫌麻煩的話,再去做個測試吧,在上面的測試程序中用一個double類型(長度為8byte)和一個long double類型(長度為10byte)做可變參數。發現什么?double類型占了8byte,而long double占了12byte。好像都是4的整數倍哦。不得不引出另一個概念了“對齊(alignment)”,所謂對齊,對Intel80x86 機器來說就是要求每個變量的地址都是sizeof(int)的倍數。原來我們搞錯了,char類型的參數只占了1byte,但是它后面的參數因為對齊的關系只能跳過3byte存儲,而那3byte也就浪費掉了。那為什么要對齊?因為在對齊方式下,CPU 的運行效率要快得多(舉個例子吧,要說明的是下面的例子是我從網上摘錄下來的,不記得出處了。

            示例:如下圖,當一個long 型數(如圖中long1)在內存中的位置正好與內存的字邊界對齊時,CPU 存取這個數只需訪問一次內存,而當一個long 型數(如圖中的long2)在內存中的位置跨越了字邊界時,CPU 存取這個數就需要多次訪問內存,如i960cx 訪問這樣的數需讀內存三次(一個BYTE、一個SHORT、一個BYTE,由CPU 的微代碼執行,對軟件透明),所以對齊方式下CPU 的運行效率明顯快多了。

            1       8       16      24      32  

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

            | long1 | long1 | long1 | long1 |

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

            |        |        |         | long2 |

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

            | long2 | long2 | long2 |        |

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

            | ....)。好像扯得有點遠來,但是有助于對_INTSIZEOF(n)的理解。位操作對于我來說是玄的東東。單個位運算還應付得來,而這樣一個表達式擺在面前就暈了。怎么辦?菜鳥自有菜的辦法。(待續)


            Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=14721

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

            C語言中的可變參數函數    CSDN Blog推出文章指數概念,文章指數是對Blog文章綜合評分后推算出的,綜合評分項分別是該文章的點擊量,回復次數,被網摘收錄數量,文章長度和文章類型;滿分100,每月更新一次。

            第一篇

            C語言編程中有時會遇到一些參數個數可變的函數,例如printf()函數,其函數原型為:

            int printf( const char* format, ...);

            它除了有一個參數format固定以外,后面跟的參數的個數和類型是可變的(用三個點“…”做參數占位符),實際調用時可以有以下的形式:

            printf("%d",i);
            printf("%s",s);
            printf("the number is %d ,string is:%s", i, s);  

            一個簡單的可變參數的C函數

                 先看例子程序。該函數至少有一個整數參數,其后占位符…,表示后面參數的個數不定。在這個例子里,所有的輸入參數必須都是整數,函數的功能只是打印所有參數的值。函數代碼如下:

            //示例代碼1:可變參數函數的使用
            #include "stdio.h"
            #include "stdarg.h"
            void simple_va_fun(int start, ...)
            {
                va_list arg_ptr;
                int nArgValue =start;
                int nArgCout="0"; //可變參數的數目
                va_start(arg_ptr,start); //以固定參數的地址為起點確定變參的內存起始地址。
                do
                {
                    ++nArgCout;
                    printf("the %d th arg: %d",nArgCout,nArgValue); //輸出各參數的值
                    nArgValue = va_arg(arg_ptr,int); //得到下一個可變參數的值
                } while(nArgValue != -1);              
                return;
            }
            int main(int argc, char* argv[])
            {
                simple_va_fun(100,-1);
                simple_va_fun(100,200,-1);
                return 0;
            }

            下面解釋一下這些代碼。從這個函數的實現可以看到,我們使用可變參數應該有以下步驟:

            ⑴由于在程序中將用到以下這些宏:
            void va_start( va_list arg_ptr, prev_param );
            type va_arg( va_list arg_ptr, type );
            void va_end( va_list arg_ptr );
            va在這里是variable-argument(可變參數)的意思。
            這些宏定義在stdarg.h中,所以用到可變參數的程序應該包含這個頭文件。

            ⑵函數里首先定義一個va_list型的變量,這里是arg_ptr,這個變量是存儲參數地址的指針.因為得到參數的地址之后,再結合參數的類型,才能得到參數的值。

            ⑶然后用va_start宏初始化⑵中定義的變量arg_ptr,這個宏的第二個參數是可變參數列表的前一個參數,即最后一個固定參數。

            ⑷然后依次用va_arg宏使arg_ptr返回可變參數的地址,得到這個地址之后,結合參數的類型,就可以得到參數的值。

            ⑸設定結束條件,這里的條件就是判斷參數值是否為-1。注意被調的函數在調用時是不知道可變參數的正確數目的,程序員必須自己在代碼中指明結束條件。至于為什么它不會知道參數的數目,在看完這幾個宏的內部實現機制后,自然就會明白。

            第二篇

            C語言之可變參數問題



            C語言中有一種長度不確定的參數,形如:"…",它主要用在參數個數不確定的函數中,我們最容易想到的例子是printf函數。

              原型:

              int printf( const char *format [, argument]... );

              使用例:

              printf("Enjoy yourself everyday!\n");

              printf("The value is %d!\n", value);

              這種可變參數可以說是C語言一個比較難理解的部分,這里會由幾個問題引發一些對它的分析。

              注意:在C++中有函數重載(overload)可以用來區別不同函數參數的調用,但它還是不能表示任意數量的函數參數。

              問題:printf的實現

              請問,如何自己實現printf函數,如何處理其中的可變參數問題? 答案與分析:

              在標準C語言中定義了一個頭文件專門用來對付可變參數列表,它包含了一組宏,和一個va_list的typedef聲明。一個典型實現如下:

              typedef char* va_list;

              #define va_start(list) list = (char*)&va_alist

              #define va_end(list)

              #define va_arg(list, mode)\

              ((mode*) (list += sizeof(mode)))[-1]

              自己實現printf:

              #include

              int printf(char* format, …)

              {

              va_list ap;

              va_start(ap, format);

              int n = vprintf(format, ap);

              va_end(ap);

              return n;

              }

              問題:運行時才確定的參數

              有沒有辦法寫一個函數,這個函數參數的具體形式可以在運行時才確定?

              答案與分析:

              目前沒有"正規"的解決辦法,不過獨門偏方倒是有一個,因為有一個函數已經給我們做出了這方面的榜樣,那就是main(),它的原型是:

              int main(int argc,char *argv[]);
            函數的參數是argc和argv。

              深入想一下,"只能在運行時確定參數形式",也就是說你沒辦法從聲明中看到所接受的參數,也即是參數根本就沒有固定的形式。常用的辦法是你可以通過定義一個void *類型的參數,用它來指向實際的參數區,然后在函數中根據根據需要任意解釋它們的含義。這就是main函數中argv的含義,而argc,則用來表明實際的參數個數,這為我們使用提供了進一步的方便,當然,這個參數不是必需的。

              雖然參數沒有固定形式,但我們必然要在函數中解析參數的意義,因此,理所當然會有一個要求,就是調用者和被調者之間要對參數區內容的格式,大小,有效性等所有方面達成一致,否則南轅北轍各說各話就慘了。

              問題:可變長參數的傳遞

              有時候,需要編寫一個函數,將它的可變長參數直接傳遞給另外的函數,請問,這個要求能否實現?

              答案與分析:

              目前,你尚無辦法直接做到這一點,但是我們可以迂回前進,首先,我們定義被調用函數的參數為va_list類型,同時在調用函數中將可變長參數列表轉換為va_list,這樣就可以進行變長參數的傳遞了。看如下所示:

              void subfunc (char *fmt, va_list argp)

              {

              ...

              arg = va_arg (fmt, argp); /* 從argp中逐一取出所要的參數 */

              ...

              }

              void mainfunc (char *fmt, ...)

              {

              va_list argp;

              va_start (argp, fmt); /* 將可變長參數轉換為va_list */

              subfunc (fmt, argp); /* 將va_list傳遞給子函數 */

              va_end (argp);

              ...

              }

              問題:可變長參數中類型為函數指針

              我想使用va_arg來提取出可變長參數中類型為函數指針的參數,結果卻總是不正確,為什么?

              答案與分析:

              這個與va_arg的實現有關。一個簡單的、演示版的va_arg實現如下:

              #define va_arg(argp, type) \

              (*(type *)(((argp) += sizeof(type)) - sizeof(type)))

              其中,argp的類型是char *。

              如果你想用va_arg從可變參數列表中提取出函數指針類型的參數,例如

              int (*)(),則va_arg(argp, int (*)())被擴展為:

              (*(int (*)() *)(((argp) += sizeof (int (*)())) -sizeof (int (*)())))

              顯然,(int (*)() *)是無意義的。

              解決這個問題的辦法是將函數指針用typedef定義成一個獨立的數據類型,例如:

              typedef int (*funcptr)();

              這時候再調用va_arg(argp, funcptr)將被擴展為:

              (* (funcptr *)(((argp) += sizeof (funcptr)) - sizeof (funcptr)))

              這樣就可以通過編譯檢查了。

              問題:可變長參數的獲取

              有這樣一個具有可變長參數的函數,其中有下列代碼用來獲取類型為float的實參:

              va_arg (argp, float);

              這樣做可以嗎?

              答案與分析:

              不可以。在可變長參數中,應用的是"加寬"原則。也就是float類型被擴展成double;char, short被擴展成int。因此,如果你要去可變長參數列表中原來為float類型的參數,需要用va_arg(argp, double)。對char和short類型的則用va_arg(argp, int)。

              問題:定義可變長參數的一個限制

              為什么我的編譯器不允許我定義如下的函數,也就是可變長參數,但是沒有任何的固定參數?

              int f (...)

              {

              ...

              }

              答案與分析:

              不可以。這是ANSI C 所要求的,你至少得定義一個固定參數。

              這個參數將被傳遞給va_start(),然后用va_arg()和va_end()來確定所有實際調用時可變長參數的類型和值。---------------------------------------------------------------------------------------------------------------------
            如何判別可變參數函數的參數類型?

            函數形式如下:
            void   fun(char*   str,...)
            {
                  ......
            }

            若傳的參數個數大于1,如何判別第2個以后傳參的參數類型???
            最好有源碼說明!


            沒辦法判斷的


            如樓上所說,例如printf( "%d%c%s ",   ....)是通過格式串中的%d,   %c,   %s來確定后面參數的類型,其實你也可以參考這種方法來判斷不定參數的類型.


            無法判斷。可變參數實現主要通過三個宏實現:va_start,   va_arg,   va_end。


            六、 擴展與思考

            個數可變參數在聲明時只需"..."即可;但是,我們在接受這些參數時不能"..."。va函數實現的關鍵就是如何得到參數列表中可選參數,包括參數的值和類型。以上的所有實現都是基于來自stdarg.h的va_xxx的宏定義。 <思考>能不能不借助于va_xxx,自己實現VA呢?,我想到的方法是匯編。在C中,我們當然就用C的嵌入匯編來實現,這應該是可以做得到的。至于能做到什么程度,穩定性和效率怎么樣,主要要看你對內存和指針的控制了。

            參考資料

            1.IEEE和OpenGroup聯合開發的Single Unix specification Ver3;BR>
            2.Linux man手冊;

            3.x86匯編,還有一些安全編碼方面的資料。

            ---------------------------------------------------------------------------------------------------------------------
            [轉帖]對C/C++可變參數表的深層探索

            C/C++語言有一個不同于其它語言的特性,即其支持可變參數,典型的函數如printf、scanf等可以接受數量不定的參數。如:
              printf ( "I love you" );
              printf ( "%d", a );
              printf ( "%d,%d", a, b );
              第一、二、三個printf分別接受1、2、3個參數,讓我們看看printf函數的原型:
              int printf ( const char *format, ... );
              從函數原型可以看出,其除了接收一個固定的參數format以外,后面的參數用"…"表示。在C/C++語言中,"…"表示可以接受不定數量的參數,理論上來講,可以是0或0以上的n個參數。
              本文將對C/C++可變參數表的使用方法及C/C++支持可變參數表的深層機理進行探索。

              一. 可變參數表的用法
              1、相關宏
              標準C/C++包含頭文件stdarg.h,該頭文件中定義了如下三個宏:
            void va_start ( va_list arg_ptr, prev_param ); /* ANSI version */
            type va_arg ( va_list arg_ptr, type );
            void va_end ( va_list arg_ptr );
              在這些宏中,va就是variable argument(可變參數)的意思;arg_ptr是指向可變參數表的指針;prev_param則指可變參數表的前一個固定參數;type為可變參數的類型。va_list也是一個宏,其定義為typedef char * va_list,實質上是一 char型指針。char型指針的特點是++、--操作對其作用的結果是增1和減1(因為sizeof(char)為1),與之不同的是int等其它類型指針的++、--操作對其作用的結果是增sizeof(type)或減sizeof(type),而且sizeof (type)大于1。
              通過va_start宏我們可以取得可變參數表的首指針,這個宏的定義為:
            #define va_start ( ap, v ) ( ap = (va_list)&v + _INTSIZEOF(v) )
              顯而易見,其含義為將最后那個固定參數的地址加上可變參數對其的偏移后賦值給ap,這樣ap就是可變參數表的首地址。其中的_INTSIZEOF宏定義為:
            #define _INTSIZEOF(n) ((sizeof ( n ) + sizeof ( int ) - 1 ) & ~( sizeof( int ) - 1 ) )
              va_arg宏的意思則指取出當前arg_ptr所指的可變參數并將ap指針指向下一可變參數,其原型為:
            #define va_arg(list, mode) ((mode *)(list =(char *) ((((int)list + (__builtin_alignof(mode)<=4?3:7)) &(__builtin_alignof(mode)<=4?-4:-8))+sizeof(mode))))[-1]
              對這個宏的具體含義我們將在后面深入討論。
              而va_end宏被用來結束可變參數的獲取,其定義為:
            #define va_end ( list )
              可以看出,va_end ( list )實際上被定義為空,沒有任何真實對應的代碼,用于代碼對稱,與va_start對應;另外,它還可能發揮代碼的"自注釋"作用。所謂代碼的"自注釋",指的是代碼能自己注釋自己。
              下面我們以具體的例子來說明以上三個宏的使用方法。
              2、一個簡單的例子
              #include <stdarg.h>
              /* 函數名:max
              * 功能:返回n個整數中的最大值
              * 參數:num:整數的個數 ...:num個輸入的整數
              * 返回值:求得的最大整數
              */
              int max ( int num, ... )
              {
               int m = -0x7FFFFFFF; /* 32系統中最小的整數 */
               va_list ap;
               va_start ( ap, num );
               for ( int i= 0; i< num; i++ )
               {
                int t = va_arg (ap, int);
                if ( t > m )
                {
                 m = t;
                }
               }
               va_end (ap);
               return m;
              }
              /* 主函數調用max */
              int main ( int argc, char* argv[] )
              {
               int n = max ( 5, 5, 6 ,3 ,8 ,5); /* 求5個整數中的最大值 */
               cout << n;
               return 0;
              }
              函數max中首先定義了可變參數表指針ap,而后通過va_start ( ap, num )取得了參數表首地址(賦給了ap),其后的for循環則用來遍歷可變參數表。這種遍歷方式與我們在數據結構教材中經常看到的遍歷方式是類似的。
              函數max看起來簡潔明了,但是實際上printf的實現卻遠比這復雜。max函數之所以看起來簡單,是因為:
              (1) max函數可變參數表的長度是已知的,通過num參數傳入;
              (2) max函數可變參數表中參數的類型是已知的,都為int型。
              而printf函數則沒有這么幸運。首先,printf函數可變參數的個數不能輕易的得到,而可變參數的類型也不是固定的,需由格式字符串進行識別(由%f、%d、%s等確定),因此則涉及到可變參數表的更復雜應用。
              下面我們以實例來分析可變參數表的高級應用。

              二. 高級應用
              下面這個程序是我們為某嵌入式系統(該系統中CPU的字長為16位)編寫的在屏幕上顯示格式字符串的函數DrawText,它的用法類似于 int printf ( const char *format, ... )函數,但其輸出的目標為嵌入式系統的液晶顯示屏幕(LED)。
              ///////////////////////////////////////////////////////////////////////////////
              // 函數名稱: DrawText
              // 功能說明: 在顯示屏上繪制文字
              // 參數說明: xPos ---橫坐標的位置 [0 .. 30]
              // yPos ---縱坐標的位置 [0 .. 64]
              // ... 可以同數字一起顯示,需設置標志(%d、%l、%x、%s)
              ///////////////////////////////////////////////////////////////////////////////
              extern void DrawText ( BYTE xPos, BYTE yPos, LPBYTE lpStr, ... )
              {
               BYTE lpData[100]; //緩沖區
               BYTE byIndex;
               BYTE byLen;
               DWORD dwTemp;
               WORD wTemp;
               int i;
               va_list lpParam;
               memset( lpData, 0, 100);
               byLen = strlen( lpStr );
               byIndex = 0;
               va_start ( lpParam, lpStr );
               for ( i = 0; i < byLen; i++ )
               {
                if( lpStr[i] != ’%’ ) //不是格式符開始
                {
                 lpData[byIndex++] = lpStr[i];
                }
                else
                {
                 switch (lpStr[i+1])
                 {
                  //整型
                  case ’d’:
                  case ’D’:
                   wTemp = va_arg ( lpParam, int );
                   byIndex += IntToStr( lpData+byIndex, (DWORD)wTemp );
                   i++;
                   break;
                  //長整型
                  case ’l’:
                  case ’L’:
                   dwTemp = va_arg ( lpParam, long );
                   byIndex += IntToStr ( lpData+byIndex, (DWORD)dwTemp );
                   i++;
                   break;
                  //16進制(長整型)
                  case ’x’:
                  case ’X’:
                   dwTemp = va_arg ( lpParam, long );
                   byIndex += HexToStr ( lpData+byIndex, (DWORD)dwTemp );
                   i++;
                   break;
                  default:
                   lpData[byIndex++] = lpStr[i];
                   break;
                 }
                }
               }
               va_end ( lpParam );
               lpData[byIndex] = ’#CONTENT#’;
               DisplayString ( xPos, yPos, lpData, TRUE); //在屏幕上顯示字符串lpData
              }
              在這個函數中,需通過對傳入的格式字符串(首地址為lpStr)進行識別來獲知可變參數個數及各個可變參數的類型,具體實現體現在for循環中。譬如,在識別為%d后,做的是va_arg ( lpParam, int ),而獲知為%l和%x后則進行的是va_arg ( lpParam, long )。格式字符串識別完成后,可變參數也就處理完了。
              在項目的最初,我們一直苦于不能找到一個好的辦法來混合輸出字符串和數字,我們采用了分別顯示數字和字符串的方法,并分別指定坐標,程序條理被破壞。而且,在混合顯示的時候,要給各類數據分別人工計算坐標,我們感覺頭疼不已。以前的函數為:
              //顯示字符串
              showString ( BYTE xPos, BYTE yPos, LPBYTE lpStr )
              //顯示數字
              showNum ( BYTE xPos, BYTE yPos, int num )
              //以16進制方式顯示數字
              showHexNum ( BYTE xPos, BYTE yPos, int num )
              最終,我們用DrawText ( BYTE xPos, BYTE yPos, LPBYTE lpStr, ... )函數代替了原先所有的輸出函數,程序得到了簡化。就這樣,兄弟們用得爽翻了。

              三. 運行機制探索
              通過第2節我們學會了可變參數表的使用方法,相信喜歡拋根問底的讀者還不甘心,必然想知道如下問題:
              (1)為什么按照第2節的做法就可以獲得可變參數并對其進行操作?
              (2)C/C++在底層究竟是依靠什么來對這一語法進行支持的,為什么其它語言就不能提供可變參數表呢?
              我們帶著這些疑問來一步步進行摸索。
              3.1 調用機制反匯編
              反匯編是研究語法深層特性的終極良策,先來看看2.2節例子中主函數進行max ( 5, 5, 6 ,3 ,8 ,5)調用時的反匯編:
              1. 004010C8 push 5
              2. 004010CA push 8
              3. 004010CC push 3
              4. 004010CE push 6
              5. 004010D0 push 5
              6. 004010D2 push 5
              7. 004010D4 call @ILT+5(max) (0040100a)
              從上述反匯編代碼中我們可以看出,C/C++函數調用的過程中:
              第一步:將參數從右向左入棧(第1~6行);
              第二步:調用call指令進行跳轉(第7行)。
              這兩步包含了深刻的含義,它說明C/C++默認的調用方式為由調用者管理參數入棧的操作,且入棧的順序為從右至左,這種調用方式稱為_cdecl調用。x86系統的入棧方向為從高地址到低地址,故第1至n個參數被放在了地址遞增的堆棧內。在被調用函數內部,讀取這些堆棧的內容就可獲得各個參數的值,讓我們反匯編到max函數的內部:
              int max ( int num, ...)
              {
              1. 00401020 push ebp
              2. 00401021 mov ebp,esp
              3. 00401023 sub esp,50h
              4. 00401026 push ebx
              5. 00401027 push esi
              6. 00401028 push edi
              7. 00401029 lea edi,[ebp-50h]
              8. 0040102C mov ecx,14h
              9. 00401031 mov eax,0CCCCCCCCh
              10. 00401036 rep stos dword ptr [edi]
              va_list ap;
              int m = -0x7FFFFFFF; /* 32系統中最小的整數 */
              11. 00401038 mov dword ptr [ebp-8],80000001h
              va_start ( ap, num );
              12. 0040103F lea eax,[ebp+0Ch]
              13. 00401042 mov dword ptr [ebp-4],eax
              for ( int i= 0; i< num; i++ )
              14. 00401045 mov dword ptr [ebp-0Ch],0
              15. 0040104C jmp max+37h (00401057)
              16. 0040104E mov ecx,dword ptr [ebp-0Ch]
              17. 00401051 add ecx,1
              18. 00401054 mov dword ptr [ebp-0Ch],ecx
              19. 00401057 mov edx,dword ptr [ebp-0Ch]
              20. 0040105A cmp edx,dword ptr [ebp+8]
              21. 0040105D jge max+61h (00401081)
              {
               int t= va_arg (ap, int);
               22. 0040105F mov eax,dword ptr [ebp-4]
               23. 00401062 add eax,4
               24. 00401065 mov dword ptr [ebp-4],eax
               25. 00401068 mov ecx,dword ptr [ebp-4]
               26. 0040106B mov edx,dword ptr [ecx-4]
               27. 0040106E mov dword ptr [t],edx
               if ( t > m )
                28. 00401071 mov eax,dword ptr [t]
                29. 00401074 cmp eax,dword ptr [ebp-8]
                30. 00401077 jle max+5Fh (0040107f)
                m = t;
                31. 00401079 mov ecx,dword ptr [t]
                32. 0040107C mov dword ptr [ebp-8],ecx
               }
               33. 0040107F jmp max+2Eh (0040104e)
               va_end (ap);
               34. 00401081 mov dword ptr [ebp-4],0
               return m;
               35. 00401088 mov eax,dword ptr [ebp-8]
              }
              36. 0040108B pop edi
              37. 0040108C pop esi
              38. 0040108D pop ebx
              39. 0040108E mov esp,ebp
              40. 00401090 pop ebp
              41. 00401091 ret
              分析上述反匯編代碼,對于一個真正的程序員而言,將是一種很大的享受;而對于初學者,也將使其受益良多。所以請一定要賴著頭皮認真研究,千萬不要被嚇倒!
              行1~10進行執行函數內代碼的準備工作,保存現場。第2行對堆棧進行移動;第3行則意味著max函數為其內部局部變量準備的堆棧空間為50h字節;第11行表示把變量n的內存空間安排在了函數內部局部棧底減8的位置(占用4個字節)。
              第12~13行非常關鍵,對應著va_start ( ap, num ),這兩行將第一個可變參數的地址賦值給了指針ap。另外,從第12行可以看出num的地址為ebp+0Ch;從第13行可以看出ap被分配在函數內部局部棧底減4的位置上(占用4個字節)。
              第22~27行最為關鍵,對應著va_arg (ap, int)。其中,22~24行的作用為將ap指向下一可變參數(可變參數的地址間隔為4個字節,從add eax,4可以看出);25~27行則取當前可變參數的值賦給變量t。這段反匯編很奇怪,它先移動可變參數指針,再在賦值指令里面回過頭來取先前的參數值賦給t(從mov edx,dword ptr [ecx-4]語句可以看出)。Visual C++同學玩得有意思,不知道碰見同樣的情況Visual Basic等其它同學怎么玩?
              第36~41行恢復現場和堆棧地址,執行函數返回操作。
              痛苦的反匯編之旅差不多結束了,看了這段反匯編我們總算弄明白了可變參數的存放位置以及它們被讀取的方式,頓覺全省輕松!
              2、特殊的調用約定
              除此之外,我們需要了解C/C++函數調用對參數占用空間的一些特殊約定,因為在_cdecl調用協議中,有些變量類型是按照其它變量的尺寸入棧的。
              例如,字符型變量將被自動擴展為一個字的空間,因為入棧操作針對的是一個字。
              參數n實際占用的空間為( ( sizeof(n) + sizeof(int) - 1 ) & ~( sizeof(int) - 1 ) ),這就是第2.1節_INTSIZEOF(v)宏的來歷!
              既然如此,前面給出的va_arg ( list, mode )宏為什么玩這么大的飛機就很清楚了。這個問題就留個讀者您來分析.



            http://wp1314.ycool.com/post.3001515.html

            posted @ 2010-06-17 23:52 Brandon 閱讀(1469) | 評論 (0)編輯 收藏

            c++中string的一些轉換

            這是我上次從這里看到的把它給保存了下來,希望對你有用。 
            1. c++中string到int的轉換 
            1) 在C標準庫里面,使用atoi: 

            #include <cstdlib> 
            #include <string> 

            std::string text = "152"; 
            int number = std::atoi( text.c_str() ); 
            if (errno == ERANGE) //可能是std::errno 

            //number可能由于過大或過小而不能完全存儲 

            else if (errno == ????) 
            //可能是EINVAL 

            //不能轉換成一個數字 


            2) 在C++標準庫里面,使用stringstream:(stringstream 可以用于各種數據類型之間的轉換) 

            #include <sstream> 
            #include <string> 

            std::string text = "152"; 
            int number; 
            std::stringstream ss; 


            ss < < text;//可以是其他數據類型 
            ss >> number; //string -> int 
            if (! ss.good()) 

            //錯誤發生 



            ss < < number;// int->string 
            string str = ss.str(); 
            if (! ss.good()) 

            //錯誤發生 


            3) 在Boost庫里面,使用lexical_cast: 

            #include <boost/lexical_cast.hpp> 
            #include <string> 

            try 

            std::string text = "152"; 
            int number = boost::lexical_cast < int >( text ); 

            catch( const boost::bad_lexical_cast & ) 

            //轉換失敗 
            }                      

            2.string 轉 CString 
            CString.format(”%s”, string.c_str()); 
            用c_str()確實比data()要好; 


            3.char 轉 CString 
            CString.format(”%s”, char*); 

            4.char 轉 string 
            string s(char *); 
            只能初始化,在不是初始化的地方最好還是用assign(). 


            5.string 轉 char * 
            char *p = string.c_str(); 

            6.CString 轉 string 
            string s(CString.GetBuffer()); 
            GetBuffer()后一定要ReleaseBuffer(),否則就沒有釋放緩沖區所占的空間. 

            7.字符串的內容轉換為字符數組和C—string 
            (1)  data(),返回沒有”\0“的字符串數組 
            (2)  c_str(),返回有”\0“的字符串數組 
            (3)  copy() 

            8.CString與int、char*、char[100]之間的轉換 

            (1) CString互轉int 

            將字符轉換為整數,可以使用atoi、_atoi64或atol。而將數字轉換為CString變量,可以使用CString的Format函數。如 
            CString s; 
            int i = 64; 
            s.Format(”%d”, i) 
            Format函數的功能很強,值得你研究一下。 

            void CStrDlg::OnButton1() 

              CString 
              ss=”1212.12″; 
              int temp=atoi(ss); 
              CString aa; 
              aa.Format(”%d”,temp); 
              AfxMessageBox(”var is ” + aa); 


            (2) CString互轉char* 

            ///char * TO cstring 
            CString strtest; 
            char * charpoint; 
            charpoint=”give string a value”; //? 
            strtest=charpoint; 

            ///cstring TO char * 
            charpoint=strtest.GetBuffer(strtest.GetLength()); 

            (3) 標準C里沒有string,char *==char []==string, 可以用CString.Format(”%s”,char *)這個方法來將char *轉成CString。 
                要把CString轉成char *,用操作符(LPCSTR)CString就可以了。 
                CString轉換 char[100] 
              char a[100]; 
              CString str(”aaaaaa”); 
              strncpy(a,(LPCTSTR)str,sizeof(a)); 

            posted @ 2009-05-31 12:38 Brandon 閱讀(451) | 評論 (0)編輯 收藏

            lua的隨機數問題

            http://dev.csdn.net/author/yanjun_1982/b682d53ae78846a19eb0b7751a250750.html     

               也許很多人會奇怪為什么使用LUA的時候,第一個隨機數總是固定,而且常常是最小的那個值,下面我就簡要的說明一下吧,說得不好,還請諒解。我現在使用的4.0版本的LUA,看的代碼是5.0的,呵呵

                    LUA4.0版本中的自帶函數庫中有兩個關于隨機數的函數,一個是random,一個是randomseed。random有兩個參數,用來設置隨機數的范圍,比如random(1,100)設置隨機數的范圍為1至100之間。由于C中所產生的隨機序列是固定的,并且第一個隨機數比較小,只有41。LUA重新設計了random函數,使得它可以產生范圍固定的隨機數,但由于LUA的random只是封裝了C的rand函數,使得random函數也有一定的缺陷,那就是如果random的兩個輸入參數的值相差很小的時候,那么隨機序列的第一個隨機數就會和第一個輸入參數很接近,比如第一次調用random(1,100)的時候,返回值肯定是1,只有相差大于799時,如random(1,800)第一次調用才會返回2,也是很接近1。
                    由于這個原因,為了實現真正的隨機,那么第一次就不能讓玩家調用random函數,不然玩家就可以獲得一些低概率的東西了。比如if random(1,100) == 1 then ...... do,看起來是1%的的概率,但是第一次執行的時候是100%成立的,存在一定的隱患。解決這個問題的方法有兩個,一就是第一次random函數不能讓玩家執行,二就是使用randomseed先設一個隨機種子。對于第一種方法,可能還是有一定的風險,畢竟隨機序列還是固定的,玩家第一次調用random的時候還是得到有規律的返回值。第二種方法比較安全,在服務器啟動的時候設置一個隨機種子,讓系統產生的隨機序列不相同,但使用randomseed的時候也還要注意一個問題,那就是做種子的數要足夠的大,大于10000就行了。不然randomseed所產生的隨機序列的第一個值還是很小。原因是randomseed是直接封裝了C的srand,如果種子的值太小,那么srand所產生的序列和默認序列(srand(1)所產生的序列)是相差不大的,序列的第一個值還是很小。
                    因此,只要在服務器啟動的時候調用一下randomseed(GetTime())就可以解決這個問題了。
                    還要補充一下,LUA中產生隨機數的算法還是有一些問題,比如執行random(1,3276700),它返回的值最后兩位必為0。這是由LUA本身的隨機函數算法決定的。
                    還是簡要介紹一下LUA中random函數的實現方法吧,主要由源碼中的下面兩行實現:
                    lua_Number r = (lua_Number)(rand()%RAND_MAX) / (lua_Number)RAND_MAX;
                    lua_pushnumber(L, (int)floor(r*(u-m+1))+m);
                    其中m為random函數的第一個參數,u為第二個參數。由上面的代碼可以看出,如果u-l太小,那么當r也很小的時候,r*(u-m+1)就會很小(小于1),那么再經過floor運算,最經結果就是m。這就可以解釋為什么random產生的第一個隨機數常常會很接近m。再來看看當m為0,u為327670的時候會怎樣。在上面的代碼里,RAND_MAX是一個宏,它的值是32767,也就是C語言中rand函數可以返回的最大值(不同的操作系統可能會有不一樣的最大值)。當m為0,u為327670的時候,那么返回值就是floor(r*(327671)+0),我們再假設LUA與平臺無關并且rand不會返回32767(上面用%避免了這個問題),那么r就可以簡化為rand()/RAND_MAX,代入上式為floor(rand()*327671/32767)+0,就算rand()的返回值是32766,最終的結果也只有327660.99996......,經過floor運算后,最后那位數必為0。呵呵,我叫這樣的隨機數就偽隨機數中的偽隨機數。實際上面的公式是不允許化簡的,即不能簡單地把r代入r*(u-m+1),至于為什么,呵呵,因為r的值并不是rand()/RAND_MAX的值,r是double類型的,所以它只是一個和rand()/RAND_MAX很接近的數。 

                    引用請注明出處。作者:yanjun_1982   日期:2006年10月11日

            posted @ 2009-05-13 10:58 Brandon 閱讀(2079) | 評論 (0)編輯 收藏

            關于MFC畫圖的一些總結,MFC (Draw)

                 摘要: 轉自:http://www.cnblogs.com/volnet/articles/472794.html 首先對按下鼠標的一點進行記錄,因此在WM_LBUTTONDOWN添加代碼:void CDrawView::OnLButtonDown(UINT nFlags, CPoint point){     // TODO: ...  閱讀全文

            posted @ 2009-04-30 15:07 Brandon 閱讀(5970) | 評論 (0)編輯 收藏

            Lua string一些東西

            一,函數
            string.len(s)
            string.find(s, sd, [index]) 返回i, j 表示 起始,終了位置索引。
            string.sub
            string.gsub
            string.gfind
            string.char
            string.byte
            string.format

            二,字符類和模式修飾符
            .      任意字符
            %a     字母
            %c     控制字符
            %d     數字
            %l     小寫字母
            %p     標點字符
            %s     空白符
            %u     大寫字母
            %w     字母和數字
            %x     十六進制數字
            %z     代表0的字符

            模式修飾符有四個:
            +      匹配前一字符1次或多次
            *      匹配前一字符0次或多次
            -      匹配前一字符0次或多次
            ?      匹配前一字符0次或1次

            posted @ 2009-04-29 15:51 Brandon 閱讀(1170) | 評論 (0)編輯 收藏

            郁悶的一個問題

            今天碰到個問題:
            lua里在一個串里查找“(”怎么寫,查找一個“)”可以寫作i,j = string.find(s, ")");
            而碰到“(”時是會出錯的,懷疑一詞法分析的時候一碰到“(”就壓進棧了,然后因為括號不匹配報錯? 
            也不能對“(”提供轉義

            posted @ 2009-04-28 18:14 Brandon 閱讀(229) | 評論 (0)編輯 收藏

            泛型算法

            拷貝:

            copy()
            reverse_copy()
            rotate_copy()
            remove_copy()  拷貝不等于某值的元素到另一個序列。
            remove_copy_if() 拷貝符合條件的到另一個序列。

            填充和生成:
            fill()
            fill_n() 填充序列中的n個元素。
            generate()為序列中的每個元素調用gen()函數。

            排列:
            next_permuttion() 后一個排列。
            prev_permutation()

            partition() 劃分,將滿足條件的元素移動到序列的前面。
            stable_partition()

            查找和替換:
            find()
            binary_search() 在一個已經有順序的序列上查找。
            find_if()
            search() 檢查第二個序列是否在第一個序列中出現,且順序相同。

            刪除:注意必須調用erase()來真正刪除
            remove()
            unique()刪除相鄰重復元素,最好現排序。

            合并序列:
            merge()

            數值算法:
            accumulate() 對序列的每個元素進行運算后求和。
            transform() 也可以對每個元素進行運算。
            計數:
            size()總個數。
            count()等于某值的元素個數。

            adjacent_difference 序列中的后一個減前與他相鄰的前一個得到新的序列。

            adiacent_find

             accumlate  iterator 對標志的序列中的元素之和,加到一個由 init 指定的初始值上。重載的版本不再做加法,而是傳進來的二元操作符被應用到元素上。 

            adjacent_different :創建一個新序列,該序列的每個新值都代表了當前元素與上一個元素的差。重載版本用指定的二元操作計算相鄰元素的差。 
            adjacent_find 
            :在 iterator 對標志的元素范圍內,查找一對相鄰的重復元素,如果找到返回一個 ForwardIterator ,指向這對元素的第一個元素。否則返回 last 。重載版本使用輸入的二元操作符代替相等的判斷。 
            binary_search 
            :在有序序列中查找 value ,如果找到返回 true 。重載的版本使用指定的比較函數對象或者函數指針來判斷相等。 
            copy 
            :復制序列。 
            copy_backward 
            :除了元素以相反的順序被拷貝外,別的和 copy 相同。 
            count 
            :利用等于操作符,把標志范圍類的元素與輸入的值進行比較,并返回相等元素的個數。 
            count_if 
            :對于標志范圍類的元素,應用輸入的操作符,并返回結果為 true 的次數。 
            equal 
            :如果兩個序列在范圍內的元素都相等,則 equal 返回 true 。重載版本使用輸入的操作符代替了默認的等于操作符。 
            equal_range 
            :返回一對 iterator ,第一個 iterator 表示由 lower_bound 返回的 iterator ,第二個表示由 upper_bound 返回的 iterator值。 
            fill 
            :將輸入的值的拷貝賦給范圍內的每個元素。 
            fill_n 
            :將輸入的值賦值給 first  frist+n 范圍內的元素。 
            find 
            :利用底層元素的等于操作符,對范圍內的元素與輸入的值進行比較。當匹配時,結束搜索,返回該元素的一個 InputIterator  
            find_if 
            :使用輸入的函數替代了等于操作符執行了 find  
            find_end 
            :在范圍內查找“由輸入的另外一個 iterator 對標志的第二個序列”的最后一次出現。重載版本中使用了用戶輸入的操作符替代等于操作。 
            find_first_of 
            :在范圍內查找“由輸入的另外一個 iterator 對標志的第二個序列”中的任意一個元素的第一次出現。重載版本中使用了用戶自定義的操作符。 
            for_each 
            :依次對范圍內的所有元素執行輸入的函數。 
            generate 
            :通過對輸入的函數 gen 的連續調用來填充指定的范圍。 
            generate_n 
            :填充 n 個元素。 
            includes 
            :判斷 [first1, last1) 的一個元素是否被包含在另外一個序列中。使用底層元素的 <= 操作符,重載版本使用用戶輸入的函數。 
            inner_product 
            :對兩個序列做內積 ( 對應的元素相乘,再求和 ) ,并將內積加到一個輸入的的初始值上。重載版本使用了用戶定義的操作。 
            inner_merge 
            :合并兩個排過序的連續序列,結果序列覆蓋了兩端范圍,重載版本使用輸入的操作進行排序。 
            iter_swap 
            :交換兩個 ForwardIterator 的值。 
            lexicographical_compare 
            :比較兩個序列。重載版本使用了用戶自定義的比較操作。 
            lower_bound 
            :返回一個 iterator ,它指向在范圍內的有序序列中可以插入指定值而不破壞容器順序的第一個位置。重載函數使用了自定義的比較操作。 
            max 
            :返回兩個元素中的較大的一個,重載版本使用了自定義的比較操作。 
            max_element 
            :返回一個 iterator ,指出序列中最大的元素。重載版本使用自定義的比較操作。 
            min 
            :兩個元素中的較小者。重載版本使用自定義的比較操作。 
            min_element 
            :類似與 max_element ,不過返回最小的元素。 
            merge 
            :合并兩個有序序列,并存放到另外一個序列中。重載版本使用自定義的比較。 
            mismatch 
            :并行的比較兩個序列,指出第一個不匹配的位置,它返回一對 iterator ,標志第一個不匹配的元素位置。如果都匹配,返回每個容器的 last 。重載版本使用自定義的比較操作。 
            next_permutation 
            :取出當前范圍內的排列,并將其重新排序為下一個排列。重載版本使用自定義的比較操作。 
            nth_element 
            :將范圍內的序列重新排序,使所有小于第 n 個元素的元素都出現在它前面,而大于它的都出現在后面,重載版本使用了自定義的比較操作。 
            partial_sort 
            :對整個序列做部分排序,被排序元素的個數正好可以被放到范圍內。重載版本使用自定義的比較操作。 
            partial_sort_copy 
            :與 partial_sort 相同,除了將經過排序的序列復制到另外一個容器。 
            partial_sum 
            :創建一個新的元素序列,其中每個元素的值代表了范圍內該位置之前所有元素之和。重載版本使用了自定義操作替代加法。 
            partition 
            :對范圍內元素重新排序,使用輸入的函數,把計算結果為 true 的元素都放在結果為 false 的元素之前。 
            prev_permutation 
            :取出范圍內的序列并將它重新排序為上一個序列。如果不存在上一個序列則返回 false 。重載版本使用自定義的比較操作。 
            random_shuffle 
            :對范圍內的元素隨機調整次序。重載版本輸入一個隨機數產生操作。 
            remove 
            :刪除在范圍內的所有等于指定的元素,注意,該函數并不真正刪除元素。內置數組不適合使用 remove  remove_if 函數。 
            remove_copy 
            :將所有不匹配的元素都復制到一個指定容器,返回的 OutputIterator 指向被拷貝的末元素的下一個位置。 
            remove_if 
            :刪除所有范圍內輸入操作結果為 true 的元素。 
            remove_copy_if 
            :將所有不匹配的元素拷貝到一個指定容器。 
            replace 
            :將范圍內的所有等于 old_value 的元素都用 new_value 替代。 
            replace_copy 
            :與 replace 類似,不過將結果寫入另外一個容器。 
            replace_if 
            :將范圍內的所有操作結果為 true 的元素用新值替代。 
            replace_copy_if 
            :類似與 replace_if ,不過將結果寫入另外一個容器。 
            reverse 
            :將范圍內元素重新按反序排列。 
            reverse_copy 
            :類似與 reverse ,不過將結果寫入另外一個容器。 
            rotate 
            :將范圍內的元素移到容器末尾,由 middle 指向的元素成為容器第一個元素。 
            rotate_copy 
            :類似與 rotate ,不過將結果寫入另外一個容器。 
            search 
            :給出了兩個范圍,返回一個 iterator ,指向在范圍內第一次出現子序列的位置。重載版本使用自定義的比較操作。 
            search_n 
            :在范圍內查找 value 出現 n 次的子序列。重載版本使用自定義的比較操作。 
            set_difference 
            :構造一個排過序的序列,其中的元素出現在第一個序列中,但是不包含在第二個序列中。重載版本使用自定義的比較操作。 
            set_intersection 
            :構造一個排過序的序列,其中的元素在兩個序列中都存在。重載版本使用自定義的比較操作。 
            set_symmetric_difference 
            :構造一個排過序的序列,其中的元素在第一個序列中出現,但是不出現在第二個序列中。重載版本使用自定義的比較操作。 
            set_union 
            :構造一個排過序的序列,它包含兩個序列中的所有的不重復元素。重載版本使用自定義的比較操作。 
            sort 
            :以升序重新排列范圍內的元素,重載版本使用了自定義的比較操作。 
            stable_partition 
            :與 partition 類似,不過它不保證保留容器中的相對順序。 
            stable_sort 
            :類似與 sort ,不過保留相等元素之間的順序關系。 
            swap 
            :交換存儲在兩個對象中的值。 
            swap_range 
            :將在范圍內的元素與另外一個序列的元素值進行交換。 
            transform 
            :將輸入的操作作用在范圍內的每個元素上,并產生一個新的序列。重載版本將操作作用在一對元素上,另外一個元素來自輸入的另外一個序列。結果輸出到指定的容器。 
            unique 
            :清除序列中重復的元素,和 remove 類似,它也不能真正的刪除元素。重載版本使用了自定義的操作。 
            unique_copy 
            :類似與 unique ,不過它把結果輸出到另外一個容器。 
            upper_bound 
            :返回一個 iterator ,它指向在范圍內的有序序列中插入 value 而不破壞容器順序的最后一個位置,該位置標志了一個大于 value 的值。重載版本使用了輸入的比較操作。 
            堆算法: C++ 標準庫提供的是 max-heap 。一共由以下 4 個泛型堆算法。 
            make_heap 
            :把范圍內的元素生成一個堆。重載版本使用自定義的比較操作。 
            pop_heap 
            :并不是真正的把最大元素從堆中彈出,而是重新排序堆。它把 first  last-1 交換,然后重新做成一個堆。可以使用容器的 back 來訪問被“彈出“的元素或者使用 pop_back 來真正的刪除。重載版本使用自定義的比較操作。 
            push_heap 
            :假設 first  last-1 是一個有效的堆,要被加入堆的元素在位置 last-1 ,重新生成堆。在指向該函數前,必須先把元素插入容器后。重載版本使用指定的比較。 
            sort_heap 
            :對范圍內的序列重新排序,它假設該序列是個有序的堆。重載版本使用自定義的比較操作。

            posted @ 2009-04-10 12:27 Brandon 閱讀(427) | 評論 (0)編輯 收藏

            詳解typedef 的用法

                 摘要: typedef 的用法 --摘自一位cnblog的一位大俠 用途一: 定義一種類型的別名,而不只是簡單的宏替換。可以用作同時聲明指針型的多個對象。比如: char* pa, pb;  // 這多數不符合我們的意圖,它只聲明了一個指向字符變量的指針, // 和一個字符變量; 以下則可行: t...  閱讀全文

            posted @ 2009-04-10 09:20 Brandon 閱讀(252) | 評論 (0)編輯 收藏

            CEdit里的光標

            int   nStart,   nEnd;   

            m_edit1.GetSel(nStart,   nEnd);   

            if(nStart   ==   nEnd)   
            {   
                m_edit1.SetSel(nStart
            +1,   nEnd+1);       
            }
            CEdit不支持SetCaretPos()詳見:http://support.microsoft.com/default.aspx?scid=kb;en-us;259949#appliesto

            posted @ 2009-04-03 11:14 Brandon 閱讀(1349) | 評論 (0)編輯 收藏

            僅列出標題
            共2頁: 1 2 
            <2025年5月>
            27282930123
            45678910
            11121314151617
            18192021222324
            25262728293031
            1234567

            導航

            統計

            常用鏈接

            留言簿(2)

            隨筆分類

            隨筆檔案

            文章分類

            文章檔案

            收藏夾

            IT WEB

            常用鏈接

            牛人BLOG

            學習網站

            搜索

            積分與排名

            最新評論

            閱讀排行榜

            評論排行榜

            久久国产精品一区| 伊人 久久 精品| 久久久免费观成人影院| 国产免费久久精品99re丫y| 久久午夜伦鲁片免费无码| 久久亚洲国产欧洲精品一| 久久国产乱子伦精品免费午夜| 久久人人爽人人爽人人爽| 久久无码人妻一区二区三区午夜| 色综合合久久天天综合绕视看| 久久久久久国产a免费观看黄色大片 | av午夜福利一片免费看久久 | 一本色道久久88—综合亚洲精品| 国产精品视频久久久| 久久亚洲国产成人影院网站| 国产精品无码久久久久久| 亚洲综合久久夜AV | 91久久香蕉国产熟女线看| 亚洲av成人无码久久精品| 蜜桃麻豆www久久国产精品| 狠狠色丁香久久婷婷综| 天天爽天天狠久久久综合麻豆| 久久久久亚洲精品男人的天堂| 国产麻豆精品久久一二三| 18禁黄久久久AAA片| 日韩美女18网站久久精品| 99久久精品免费看国产免费| 国产91久久精品一区二区| 久久亚洲AV成人无码国产| 久久午夜无码鲁丝片秋霞| 久久99国产精品久久99小说| 久久无码国产| 香蕉aa三级久久毛片| 久久人人爽人人爽人人片AV麻豆| 99久久精品免费看国产免费| 久久久中文字幕| 国产成人精品久久综合| 四虎国产永久免费久久| 久久精品9988| 国产三级精品久久| 久久精品二区|