可變參數的函數的原理

來源:互聯網
上載者:User

可變參數。
1:必須有一個提前參數,(即:...之前必須要有一個參數),用以計算出後面的第一個未知參數的地址. 知道了第一個未知參數的地址之後, 就可以根據fmt格式化串,可以依次計算出剩餘的參數地址.
sprintf()的原型:sprintf(char* buffer, const char* fmt, ... ) ,其中,fmt就是提前參數
2:每一個可變闡述函數,其編寫者與使用者 都要有一個參數的使用約定。不然,會亂套。

3:可變函數實現的技術基礎
1:所有參數,在彙編層級,其大小都是4個位元組的整數倍。char,short類型也被擴充成4byte
把任意長度的類型擴充成4byte的整數倍的技術就是
_INTSIZEOF(n) ( (sizeof(n)+sizeof(int)-1) & ~(sizeof(int)-1))
簡化就是,((sizeof(n)+4-1) & ~(4-1))
_INTSIZEOF(char*) = 4
_INTSIZEOF(2byte) = 4
_INTSIZEOF(double)= 8

2:有了上述的知識,下面就是,從可變參數中一個一個去參數了
#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )
               // :取得已知參數的地址,
               // ap 就是第一個未知參數地址,v就是上述的提前參數fmt
               // eg :fun( int n,...) v就是n的地址。

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

va_arg()替換後,ap指向t類型後的下一個類型的地址。va_arg本身被替換成了t類型對應參數的值. t類型一般是從fmt格式化串中根據%d,%c等轉換的.
比如1: int a = va_art(ap, int);

比如2: fun(int n,...)
{
...
char* pChar = va_arg(ap, char*)
int tmp = va_arg(ap, int)
double dd = va_arg(ap, double)
}
那麼,...必然是 char*,int,double的參數順序!!如果不是這樣的順序,程式將不定期崩潰!!這就

是為什麼c不是型別安全的,boost::Format好像提供型別安全的格式轉換,可以替代sprintf的使用。
對於sprintf()而言,va_arg(av,v)v的值,使用 char* fmt 中提取的。
 
附:

3)//範例程式碼2:擴充——自己實現簡單的可變參數的函數。(沒有用到va_list等宏)
下面是一個簡單的printf函數的實現,參考了<The C Programming Language>中的例子
#include "stdio.h"
#include "stdlib.h"
void myprintf(char* fmt, ...)        //一個簡單的類似於printf的實現,//參數必須都是int 類型
{
    char* pArg=NULL;               //等價於原來的va_list
    char c;
   
    pArg = (char*) &fmt;          //注意不要寫成p = fmt !!因為這裡要對//參數取址,而不是取值
    pArg += sizeof(fmt);         //等價於原來的va_start         
 
    do
    {
        c =*fmt;
        if (c != '%')
        {
            putchar(c);            //照原樣輸出字元
        }
        else
        {
           //按格式字元輸出資料
           switch(*++fmt)
           {
            case'd':
                printf("%d",*((int*)pArg));          
                break;
            case'x':
                printf("%#x",*((int*)pArg));
                break;
            default:
                break;
            }
            pArg += sizeof(int);               //等價於原來的va_arg
        }
        ++fmt;
    }while (*fmt != '/0');
    pArg = NULL;                               //等價於va_end
    return;
}
int main(int argc, char* argv[])
{
    int i = 1234;
    int j = 5678;
   
    myprintf("the first test:i=%d/n",i,j);
    myprintf("the secend test:i=%d; %x;j=%d;/n",i,0xabcd,j);
    system("pause");
    return 0;
}

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.