青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

woaidongmao

文章均收錄自他人博客,但不喜標題前加-[轉貼],因其丑陋,見諒!~
隨筆 - 1469, 文章 - 0, 評論 - 661, 引用 - 0
數據加載中……

從printf談可變參數函數的實現

摘要:一直以來都覺得printf似乎是c語言庫中功能最強大的函數之一,不僅因為它能格式化輸出,更在于它的參數個數沒有限制,要幾個就給幾個,來者不拒。printf這種對參數個數和參數類型的強大適應性,讓人產生了對它進行探索的濃厚興趣。

關鍵字:printf, 可變參數

1. 使用情形

 

int a =10;

 

double b = 20.0;

 

char *str = "Hello world";

 

printf("begin print\n");

 

printf("a=%d, b=%.3f, str=%s\n", a, b, str);

 

...

 

  從printf的使用情況來看,我們不難發現一個規律,就是無論其可變的參數有多少個,printf的第一個參數總是一個字符串。而正是這第一個參數,使得它可以確認后面還有有多少個參數尾隨。而尾隨的每個參數占用的??臻g大小又是通過第一個格式字符串確定的。然而printf到底是怎樣取第一個參數后面的參數值的呢,請看如下代碼

2. printf 函數的實現

 

//acenv.h

 

typedef char *va_list;

 

 

 

#define  _AUPBND        (sizeof (acpi_native_int) - 1)

 

#define  _ADNBND        (sizeof (acpi_native_int) - 1)

 

                       

 

#define _bnd(X, bnd)    (((sizeof (X))   (bnd)) & (~(bnd)))

 

#define va_arg(ap, T)   (*(T *)(((ap)  = (_bnd (T, _AUPBND))) - (_bnd (T,_ADNBND))))

 

#define va_end(ap)      (void) 0

 

#define va_start(ap, A) (void) ((ap) = (((char *) &(A))   (_bnd (A,_AUPBND))))

 

 

 

//start.c

 

static char sprint_buf[1024];

 

int printf(char *fmt, ...)

 

{

 

        va_list args;

 

        int n;

 

        va_start(args, fmt);

 

        n = vsprintf(sprint_buf, fmt, args);

 

        va_end(args);

 

        write(stdout, sprint_buf, n);

 

        return n;

 

}

 

 

 

//unistd.h

 

static inline long write(int fd, const char *buf, off_t count)

 

{

 

        return sys_write(fd, buf, count);

 

}

 

3. 分析

  從上面的代碼來看,printf似乎并不復雜,它通過一個宏va_start把所有的可變參數放到了由args指向的一塊內存中,然后再調用vsprintf. 真正的參數個數以及格式的確定是在vsprintf搞定的了。由于vsprintf的代碼比較復雜,也不是我們這里要討論的重點,所以下面就不再列出了。我們這里要討論的重點是va_start(ap, A)宏的實現,它對定位從參數A后面的參數有重大的制導意義?,F在把 #define va_start(ap, A) (void) ((ap) = (((char *) &(A)) (_bnd (A,_AUPBND)))) 的含義解釋一下如下:

 

    va_start(ap, A)

 

    {

 

         char *ap =  ((char *)(&A))   sizeof(A)int類型大小地址對齊

 

    }

 

   在printfva_start(args, fmt)中,fmt的類型為char *, 因此對于一個32為系統 sizeof(char *) = 4, 如果int大小也是32,則va_start(args, fmt);相當于 char *args = (char *)(&fmt) 4; 此時args的值正好為fmt后第一個參數的地址。對于如下的可變參數函數

 

    void fun(double d,...)

 

    {

 

        va_list args;

 

        int n;

 

        va_start(args, d);

 

    }

 

va_start(args, d);相當于

 

    char *args = (char *)&d   sizeof(double);

 

   此時args正好指向d后面的第一個參數。

  可變參數函數的實現與函數調用的棧結構有關,正常情況下c/c 的函數參數入棧規則為__stdcall, 它是從右到左的,即函數中的最右邊的參數最先入棧。對于函數

 

    void fun(int a, int b, int c)

 

    {

 

        int d;

 

        ...

 

    }

 

其棧結構為

 

    0x1ffc-->d

 

    0x2000-->a

 

    0x2004-->b

 

    0x2008-->c

 

   對于任何編譯器,每個棧單元的大小都是sizeof(int), 而函數的每個參數都至少要占一個棧單元大小,如函數 void fun1(char a, int b, double c, short d) 對一個32的系統其棧的結構就是

 

    0x1ffc-->a  (4字節)

 

    0x2000-->b  (4字節)

 

    0x2004-->c  (8字節)

 

    0x200c-->d  (4字節)

 

  對于函數void fun1(char a, int b, double c, short d)

  如果知道了參數a的地址,則要取后續參數的值則可以通過a的地址計算a后面參數的地址,然后取對應的值,而后面參數的個數可以直接由變量a指定,當然也可以像printf一樣根據第一個參數中的%模式個數來決定后續參數的個數和類型。如果參數的個數由第一個參數a直接決定,則后續參數的類型如果沒有變化并且是已知的,則我們可以這樣來取后續參數, 假定后續參數的類型都是double;

 

void fun1(int num, ...)

 

{

 

    double *p = (double *)((&num) 1);

 

    double Param1 = *p;

 

    double Param2 = *(p 1);

 

    ...

 

    double Paramn  *(p num);

 

}

 

   如果后續參數的類型是變化而且是未知的,則必須通過一個參數中設定模式來匹配后續參數的個數和類型,就像printf一樣,當然我們可以定義自己的模式,如可以用i表示int參數,d表示double參數,為了簡單,我們用一個字符表示一個參數,并由該字符的名稱決定參數的類型而字符的出現的順序也表示后續參數的順序。我們可以這樣定義字符和參數類型的映射表,

 

i---int

 

s---signed short

 

l---long

 

c---char

 

"ild"模式用于表示后續有三個參數,按順序分別為int, long, double類型的三個參數那么這樣我們可以定義自己版本的printf 如下

 

void printf(char *fmt, ...)

 

{

 

    char s[80] = "";

 

    int paramCount = strlen(fmt);

 

    write(stdout, "paramCount = " , strlen(paramCount = ));

 

    itoa(paramCount,s,10);

 

    write(stdout, s, strlen(s));

 

    char *p = (char *)(&fmt)   sizeof(char *);

 

    int *pi = (int *)p;

 

    for (int i=0; i<paramCount; i  )

 

    {

 

        char line[80] = "";

 

        strcpy(line, "param");

 

        itoa(i 1, s, 10);

 

        strcat(line, s);

 

        strcat(line, "=");

 

        switch(fmt[i])

 

        {

 

            case 'i':

 

            case 's':

 

                itoa((*pi),s,10);

 

                strcat(line, s);

 

                pi  ;

 

                break;

 

            case 'c':

 

                {

 

                    int len = strlen(line);

 

                    line[len] = (char)(*pi);

 

                    line[len 1] = '\0';

 

                }

 

                break;

 

            case 'l':

 

                ltoa((*(long *)pi),s,10);

 

                strcat(line, s);

 

                pi  ;

 

                break;

 

            default:

 

                break;

 

        }

 

    }

 

}

 

也可以這樣定義我們的Max函數,它返回多個輸入整型參數的最大值

 

int Max(int n, ...)

 

{

 

    int *p = &n   1;

 

    int ret = *p;

 

    for (int i=0; i<n; i  )

 

    {

 

        if (ret < *(p   i))

 

            ret = *(p   i);

 

    }

 

    return ret;

 

}

 

可以這樣調用, 后續參數的個數由第一個參數指定

 

int m = Max(3, 45, 12, 56);

 

int m = Max(1, 3);

 

int m = Max(2, 23, 45);

 

 

 

int first = 34, second = 45, third=5;

 

int m = Max(5, first, second, third, 100, 4);

 

結論

   對于可變參數函數的調用有一點需要注意,實際的可變參數的個數必須比前面模式指定的個數要多,或者不小于,也即后續參數多一點不要緊,但不能少, 如果少了則會訪問到函數參數以外的堆棧區域,這可能會把程序搞崩掉。前面模式的類型和后面實際參數的類型不匹配也有可能造成把程序搞崩潰,只要模式指定的數據長度大于后續參數長度,則這種情況就會發生。如:

 

printf("%.3f, %.3f, %.6e", 1, 2, 3, 4);

 

   參數1,2,3,4的默認類型為整型,而模式指定的需要為double型,其數據長度比int大,這種情況就有可能訪問函數參數堆棧以外的區域,從而造成危險。但是printf("%d, %d, %d", 1.0, 20., 3.0);這種情況雖然結果可能不正確,但是確不會造成災難性后果。因為實際指定的參數長度比要求的參數長度長,堆棧不會越界。

posted on 2009-08-12 13:05 肥仔 閱讀(619) 評論(1)  編輯 收藏 引用 所屬分類: C++ 基礎

評論

# re: 從printf談可變參數函數的實現  回復  更多評論   

錯誤百出,建議還是別看了,真正的還是沒說到點子.浪費時間!
2011-04-29 13:35 |
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            亚洲视频在线观看网站| 久久人人爽人人爽| 欧美黄色日本| 国产视频精品xxxx| 欧美高清视频免费观看| 亚洲高清一区二| 亚洲国产高清aⅴ视频| 久久一区二区精品| 可以免费看不卡的av网站| 亚洲精品免费网站| 午夜精品婷婷| 国产精品一国产精品k频道56| 欧美三级视频在线| 国产精品v欧美精品∨日韩| 亚洲精品影视| 国产精品视频一二| 亚洲自拍偷拍色片视频| 亚洲精品一区在线观看| 在线综合亚洲欧美在线视频| 免费日韩精品中文字幕视频在线| 久久蜜桃精品| 欧美在线关看| 久久免费视频在线观看| 性欧美办公室18xxxxhd| 亚洲在线免费观看| 亚洲天堂成人在线视频| 夜久久久久久| 欧美一级视频| 久久精品国产亚洲精品 | 日韩特黄影片| 国产美女精品| 欧美怡红院视频| 亚洲欧洲精品天堂一级| 亚洲免费观看高清完整版在线观看| 亚洲欧美制服另类日韩| 午夜久久福利| 欧美一级网站| 久久综合九色综合欧美狠狠| 亚洲国产91| 国产欧美日韩精品专区| 亚洲人www| 亚洲小说欧美另类社区| 亚洲欧美一区二区原创| 亚洲欧美在线一区二区| 国产精品国产三级国产普通话三级| 亚洲精品偷拍| 亚洲国产精品成人综合色在线婷婷 | 免费观看不卡av| 亚洲国产高清在线观看视频| 99精品久久久| 一本色道久久综合亚洲精品按摩| 亚洲精品在线观| 亚洲毛片网站| 午夜精品国产| 国产亚洲精品一区二区| 狂野欧美性猛交xxxx巴西| 国产精品系列在线播放| 欧美专区亚洲专区| 欧美va天堂| 国产精品一级| 欧美一区二区三区另类| 一本色道久久精品| 正在播放日韩| 久久久www成人免费无遮挡大片 | 欧美绝品在线观看成人午夜影视| 亚洲第一视频网站| 日韩亚洲欧美在线观看| 在线观看福利一区| 亚洲毛片在线观看.| 亚洲精品国产系列| 欧美一区二区网站| 国产伦精品一区二区三区视频黑人| 性欧美videos另类喷潮| 亚洲国产一区二区三区在线播 | 日韩网站在线观看| 日韩一区二区高清| 亚洲国产高潮在线观看| 欧美日韩aaaaa| 国产精品成人一区二区| 伊人久久久大香线蕉综合直播 | 亚洲激情图片小说视频| 欧美日本乱大交xxxxx| 欧美三级在线| 亚洲一区美女视频在线观看免费| 99国产精品私拍| 欧美在线观看你懂的| 国产农村妇女毛片精品久久莱园子| 欧美一级黄色网| 日韩视频在线免费| 欧美11—12娇小xxxx| 国产在线高清精品| 国产精品一区二区你懂的| 9i看片成人免费高清| 亚洲第一精品夜夜躁人人爽| 欧美精品国产精品日韩精品| 91久久亚洲| 亚洲午夜一区二区三区| 亚洲精品四区| 亚洲欧美日韩国产综合精品二区| 亚洲伦理在线观看| 国产精品久久久久婷婷| 亚洲免费成人av电影| 国产精品激情av在线播放| 亚洲视频一区在线观看| 麻豆精品视频在线观看| avtt综合网| 久久精品人人做人人爽| 国产精品理论片在线观看| 麻豆成人在线观看| 亚洲国产色一区| 亚洲免费在线视频| 久久av二区| 欧美精品一区二区三区蜜臀| 亚洲精品一区在线观看香蕉| 亚洲日本va在线观看| 欧美久久久久久蜜桃| 在线中文字幕不卡| 欧美激情1区2区| 日韩午夜av电影| 免费h精品视频在线播放| 国内揄拍国内精品少妇国语| 欧美极品在线观看| 亚洲欧美国产一区二区三区| 欧美日本簧片| 久久大综合网| 亚洲日本一区二区| 欧美日韩1234| 亚洲免费高清| 久久久久五月天| 中文精品一区二区三区| 美女主播视频一区| 欧美不卡视频一区| 国产精品av久久久久久麻豆网| 亚洲欧美视频在线观看| 亚洲全部视频| 欧美日韩一区二区三区免费看| 欧美承认网站| 国产乱码精品一区二区三区五月婷 | 欧美精品午夜| 亚洲区中文字幕| 国产精品免费网站在线观看| 久久久xxx| 亚洲曰本av电影| 激情综合中文娱乐网| 久久久精彩视频| 亚洲天堂av高清| 小辣椒精品导航| 免费看的黄色欧美网站| 欧美精品电影在线| 久久精品国产99国产精品澳门 | 鲁大师影院一区二区三区| 欧美亚男人的天堂| 黄色亚洲免费| 日韩午夜免费| 欧美日韩高清免费| 亚洲国产成人精品女人久久久| 国产精品福利片| 国产一区二区三区不卡在线观看| 亚洲大片在线| 女同一区二区| 精品不卡一区| 欧美国产综合一区二区| 欧美电影在线免费观看网站| 久久深夜福利免费观看| 国产精品老牛| 亚洲欧美日韩另类| 欧美另类一区二区三区| 99re8这里有精品热视频免费| 久久精品免视看| 国产免费成人av| 欧美成人激情视频| 亚洲一区二区三区涩| 久久婷婷av| 久久久天天操| 欧美成人伊人久久综合网| 夜夜嗨一区二区| 欧美国产免费| 久久精品麻豆| 亚洲人成免费| 亚洲国产日韩欧美在线动漫| 在线播放中文字幕一区| 国产亚洲观看| 久久久噜久噜久久综合| 麻豆精品精品国产自在97香蕉| 久久综合中文| 欧美韩日一区二区| 久久综合伊人| 亚洲开发第一视频在线播放| 亚洲欧美激情一区| 久久五月激情| 欧美日韩亚洲综合| 欧美国产第一页| 国产精品欧美一区二区三区奶水| 欧美国产精品中文字幕| 欧美极品一区| 亚洲精品久久在线| 国产精品综合网站| 欧美精品一区二区三| 亚洲一区二区三区涩| 亚洲视频导航|