可變參函式(my printf可變參函式的實現)

2021-08-18 14:21:27 字數 4571 閱讀 5202

可變參函式:

其引數列表的引數型別與個數可變,採用ansi標準形式時,引數個數可變的函式的原型宣告是:

type funcname(type para1, type para2, ...)

//至少需要乙個普通的形式引數,後面的省略號不表示省略,而是函式原型的一部分,為引數佔位符,type是函式返回值和形式引數的型別

可變參函式的實現:1、採用ansi標準形式

2、可變引數列表:

通過定義在stdarg.h標頭檔案(標準庫的一部分)的巨集來實現。

1)乙個型別: va_list

typedef

char * va_list;

//用來儲存巨集va_start、va_arg和va_end所需資訊的一種型別;

//為了訪問變長引數列表中的引數,必須宣告 。

#define  va_start   _crt_va_start

#define _crt_va_start(ap,v) ( ap = (va_list)_addressof(v) + _intsizeof(v) )

#define _addressof(v) ( &reinterpret_cast(v) )

//相當於 &v

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

//訪問變長引數列表中的引數之前使用的巨集;

//初始化用va_list宣告的物件,初始化結果供巨集va_arg和 va_end使用

(2)va_arg

#define va_arg _crt_va_arg

#define _crt_va_arg(ap,t) ( *(t *)((ap += _intsizeof(t)) - _intsizeof(t)) )

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

//展開成乙個表示式的巨集;

//該表示式具有變長引數列表中下乙個引數的值和型別,每次呼叫va_arg都會修改

(3)va_end

#define va_end _crt_va_end

#define _crt_va_end(ap) ( ap = (va_list)0 )

//展開成乙個表示式的巨集;

//該表示式具有變長引數列表中下乙個引數的值和型別。每次呼叫va_arg都會修改

3)可變引數在編譯器中的處理:

(1)首先把va_list被定義成char*(我們目前所用的pc機上,字元指標型別可以用來儲存記憶體單元位址),有的機器上va_list是被定義成void*。

(2)定義_intsizeof(n)主要是為了某些需要記憶體的對齊的系統,為了得到最後乙個固定引數的實際記憶體大小(可直接用sizeof運算子來代替)。

(3)va_start的定義為 &v+_intsizeof(v) ,這裡&v是最後乙個固定引數的起始位址,再加上其實際占用大小後,目的為了得到第乙個可變引數的起始記憶體位址;

當執行va_start(ap, v)後,此時ap指向第乙個可變引數在的記憶體位址。

(4)va_arg由執行va_start(ap, v)後,取得第乙個可變引數的位址,ap訪問該位址根據指定的引數型別取得本引數的值,並且把指標調到下乙個引數的起始位址。

①用使用者輸入的型別名對引數位址進行強制型別轉換,得到使用者所需要的值;

②計算出本引數的實際大小後將指標調到本引數的結尾,即下乙個引數的首位址,便於處理下乙個引數。

(5)va_end的定義相當於((void*)0),使ap不再 指向堆疊,而是置null。

3、重要因數:

①函式棧的生長方向:向下生長,棧頂指標的記憶體位址低於棧底指標,所以先進棧的資料是存放在記憶體的高位址處,從棧底向棧頂看過去,位址是從高位址走向低位址的,因為稱它為向下生長

(棧由編譯器自動管理,其中存放的是函式引數及區域性變數)

②引數的入棧順序:自右向左,並且連續儲存

引數進棧記憶體模型如下:

記憶體位址 內容

高記憶體位址處 最後乙個可變引數 –棧頂出

…… 第n個可變引數 –va_arg(arg_ptr,int),呼叫第n個可變引數的位址

…… 第乙個可變引數 –va_start(arg_ptr, start),得第乙個可變引數位址

最後乙個固定引數 –start的起始位址

…… 低記憶體位址處 第乙個固定引數

注意:最後乙個固定變數不能宣告為暫存器型別變數,函式型別變數或陣列型別變數

③cpu的對齊方式

④記憶體位址的表達方式

4、舉例說明:printf函式

//原型:printf(const char * _format, ...);

int main()

//反彙編

c 語言中可變長引數的處理心得

#include

#include

#include

//va_start(ap, v)後,此時ap指向第乙個可變引數在的記憶體位址

//va_end(ap), ap不再 指向堆疊,而是置null

//列印%d

void print_int(const

int num)

//itoa逆序輸出(先遞迴,後輸出實現)

else

}//列印%c

void print_char(const

char ch)

//列印%s

void print_str(const

char *str)

}//列印%f

void print_float(const

float f)

else

}void my_printf(const

char *format, ...)

else

case

'c':

case

's':

case

'f':

default:

//原樣輸出語句}}

}va_end(ap);

}int main()

可變參函式

int add int x,int main int add int x,int sum 0 char point char x for int i 0 iint add int x,可變參函式原型,該函式中帶有識別符號的引數 x記錄的是引數的個數,後面的數字是需要求和的數。x的作用是為了標誌出加數...

可變參函式

採用c語言程式設計的時候,函式中形式引數的數目通常是確定的,在呼叫時要依次給出與形式引數對應的所有實際引數。但在某些情況下希望函式的引數個數可以根據需要確定。例如 printf const char format,c語言可變引數通過三個巨集 va start va end va arg 和乙個型別 ...

可變形參函式

在c 程式設計中,有時我們需要編寫一些在源 編寫階段無法確定引數個數,有時甚至無法確定引數型別的函式。因此我們需要一類函式 它們可以在執行時取任意的實參個數並根據實參的個數自動處理不同實參的情形,或者至少可以在執行時指定任意的實參個數。在c 中實現乙個變參函式的方法有三種 重點介紹第一種方法和第三種...