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

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的第一個參數總是一個字符串。而正是這第一個參數,使得它可以確認后面還有有多少個參數尾隨。而尾隨的每個參數占用的棧空間大小又是通過第一個格式字符串確定的。然而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后面的參數有重大的制導意義。現在把 #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);

 

   參數1234的默認類型為整型,而模式指定的需要為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>
            亚洲人成毛片在线播放| 一区二区日韩精品| 韩国福利一区| 国产乱理伦片在线观看夜一区| 欧美日韩免费观看一区三区 | 午夜国产欧美理论在线播放| 亚洲系列中文字幕| 亚洲欧美中文字幕| 久久精品99| 另类酷文…触手系列精品集v1小说| 久久精品免费观看| 欧美成人xxx| 国产精品jizz在线观看美国 | 欧美性猛交xxxx乱大交蜜桃| 国产精品日日摸夜夜添夜夜av| 国产视频在线观看一区二区| 激情欧美一区| 在线亚洲欧美专区二区| 巨胸喷奶水www久久久免费动漫| 久久婷婷综合激情| 欧美日韩在线视频一区| 国产视频精品网| 亚洲人成网站999久久久综合| 在线亚洲一区观看| 免费久久99精品国产自| 一区二区三区免费网站| 久久人人爽人人爽爽久久| 欧美日韩视频在线一区二区观看视频| 国产欧美一区二区视频| 日韩天堂在线观看| 久久久免费精品| 91久久精品国产91性色| 欧美一区1区三区3区公司| 欧美国产日韩a欧美在线观看| 国产精品手机在线| 亚洲最新视频在线| 欧美成人精品福利| 欧美一区二区视频免费观看| 欧美日韩久久| 亚洲国内欧美| 久久这里只有| 羞羞答答国产精品www一本| 欧美日韩亚洲综合| 日韩一区二区免费看| 免费人成精品欧美精品| 亚洲影音一区| 免费亚洲网站| 久久精品国产免费看久久精品| 欧美香蕉大胸在线视频观看| 亚洲国产日韩欧美在线动漫| 久久久之久亚州精品露出| 亚洲在线视频一区| 国产精品videosex极品| 99精品视频免费全部在线| 欧美高清影院| 免费久久精品视频| 亚洲激情视频在线观看| 欧美激情一区二区三区成人| 久久久91精品国产一区二区三区| 国产精品视频一| 午夜久久久久久| 一区二区不卡在线视频 午夜欧美不卡'| 欧美激情黄色片| 99riav1国产精品视频| 亚洲黄色视屏| 欧美色大人视频| 亚洲在线第一页| 亚洲自拍偷拍福利| 国产日韩欧美在线播放| 久久精品国产99精品国产亚洲性色 | 日韩小视频在线观看| 欧美成人午夜激情| 久久av一区二区三区| 伊大人香蕉综合8在线视| 欧美jizz19性欧美| 毛片av中文字幕一区二区| 亚洲理论电影网| 一本久道综合久久精品| 国产欧美一区二区三区另类精品| 久久精品国产精品 | 亚洲视频一区二区在线观看 | 欧美成人免费在线观看| 欧美黄色日本| 亚洲视频久久| 亚洲综合日韩在线| 国产综合久久| 91久久黄色| 国产欧美一区二区精品忘忧草| 久久精品视频播放| 理论片一区二区在线| 亚洲专区免费| 久久久久亚洲综合| 国产精品99久久99久久久二8| 午夜精品久久久久久久蜜桃app| 亚洲黄色免费| 亚洲欧美综合一区| 亚洲麻豆视频| 久久精品视频va| 亚洲视频在线观看一区| 欧美一区国产在线| 欧美麻豆久久久久久中文| 欧美在线视频在线播放完整版免费观看| 欧美在线www| 99re6这里只有精品| 久久成人免费网| 亚洲一区在线播放| 麻豆精品一区二区av白丝在线| 亚洲你懂的在线视频| 麻豆精品精华液| 欧美在线免费视屏| 欧美日韩在线一区二区三区| 美女日韩在线中文字幕| 国产精品久久久久免费a∨大胸| 欧美成人免费在线视频| 国内精品久久久久久 | 久久亚洲精品一区二区| 国产精品成人久久久久| 亚洲高清视频中文字幕| 国内久久婷婷综合| 亚洲一区二区视频在线| 9色精品在线| 麻豆精品国产91久久久久久| 久久久久国产免费免费| 国产精品羞羞答答| 一道本一区二区| 夜夜嗨av一区二区三区四季av| 裸体素人女欧美日韩| 美女福利精品视频| 国产一区二区视频在线观看| 亚洲调教视频在线观看| 亚洲视频在线观看一区| 欧美日韩国产成人在线| 亚洲青色在线| 99爱精品视频| 欧美激情一二三区| 91久久精品日日躁夜夜躁国产| 亚洲国产精品电影| 免费的成人av| 亚洲日本激情| 亚洲无线视频| 国产精品少妇自拍| 午夜激情综合网| 久久久xxx| 亚洲高清三级视频| 欧美国产日本韩| 亚洲人成人一区二区三区| 日韩视频在线免费观看| 欧美精品一区二区三区视频| 亚洲精品国产精品久久清纯直播| 亚洲看片免费| 欧美色欧美亚洲另类二区| 一区二区三区不卡视频在线观看| 亚洲一品av免费观看| 国产精品永久免费在线| 欧美伊人精品成人久久综合97| 久久中文字幕一区| 亚洲美女91| 国产精品久久久久影院色老大 | 国产美女精品在线| 久久激情视频久久| 亚洲大片免费看| 99国产精品久久久久久久| 国产精品二区影院| 久久狠狠一本精品综合网| 亚洲日本电影在线| 欧美三级日本三级少妇99| 亚洲欧美在线一区| 欧美电影专区| 亚洲天堂久久| 一区二区三区在线观看视频 | 欧美一区二区在线观看| 亚洲国产成人久久综合| 亚洲欧美欧美一区二区三区| 激情久久五月| 国产精品美女久久久久av超清| 久久久久成人精品| 一区二区三区导航| 你懂的亚洲视频| 午夜在线播放视频欧美| 亚洲日本免费| 黄色成人av| 国产精品久久国产精品99gif| 久久久青草婷婷精品综合日韩| 夜夜精品视频一区二区| 美日韩精品免费| 久久精品国产免费看久久精品| 99热在线精品观看| 精品电影在线观看| 国产精品国产三级国产专播品爱网| 久久视频国产精品免费视频在线| 一区二区三区 在线观看视| 亚洲高清不卡一区| 久久久久久免费| 亚洲欧美卡通另类91av| 99re在线精品| 日韩网站在线看片你懂的| 1024成人网色www| 狠狠入ady亚洲精品经典电影| 欧美午夜一区二区三区免费大片 | 亚洲精品国久久99热|