C語言——可變引數
C語言——可變引數
宗旨:技術的學習是有限的,分享的精神是無限的。
1、目前為止,見過比較熟悉的可變引數的函式就是printf()函式
int printf(const char *format, …);<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"> </span>
2、可變引數巨集
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_list:用來儲存巨集va_start、va_arg和va_end所需資訊的一種型別。為了訪問變長引數列表中的引數,必須宣告va_list型別的一個物件 定義: typedef char * va_list;
va_start:訪問變長引數列表中的引數之前使用的巨集,它初始化用va_list宣告的物件,初始化結果供巨集va_arg和va_end使用;
va_arg: 展開成一個表示式的巨集,該表示式具有變長引數列表中下一個引數的值和型別。每次呼叫va_arg都會修改用va_list宣告的物件,從而使該物件指向引數列表中的下一個引數;
va_end:該巨集使程式能夠從變長引數列表用巨集va_start引用的函式中正常返回。
C語言記憶體管理中,我們提到函式的引數存放在棧中。通過反彙編可知,可變引數是從右向左依次壓棧的,所以第一個引數靠近棧頂,最後一個引數靠近棧底。這些引數在記憶體中是連續存放的,每個引數都4位元組對齊。
3、stdarg.h的一種實現
/* stdarg.h standard header */ #ifndef _STDARG #define _STDARG /* type definitions */ typedef char *va_list; /* macros */ #define va_arg(ap, T) \ (* (T *)(((ap) += _Bnd(T, 3U)) - _Bnd(T, 3U))) #define va_end(ap) (void)0 #define va_start(ap, A) \ (void)((ap) = (char *)&(A) + _Bnd(A, 3U)) #define _Bnd(X, bnd) (sizeof (X) + (bnd) & ~(bnd)) #endif /*<span style="font-family: Arial, Helvetica, sans-serif;">_STDARG</span> */
這個標頭檔案中的內部巨集定義將型別或變數的長度對齊到位元組的整數倍,例如_Bnd(char, 3U)的值是4,_Bnd(int, 3U)也是4
4、可變引數函式實現思路
(1)首先在函式裡定義一個va_list型的變數,這裡是arg_ptr,這個變量是指向引數的指標。(2)然後用va_start巨集初始化變數arg_ptr,這個巨集的第二個引數是第一個可變引數的前一個引數,是一個固定的引數。
(3)然後用va_arg返回可變的引數,並賦值給整數j. va_arg的第二個引數是你要返回的引數的型別,這裡是int型。(4)最後用va_end巨集結束可變引數的獲取.然後你就可以在函式裡使用第二個引數了.如果函式有多個可變引數的,依次呼叫va_arg獲取各個引數。
5、myprintf函式實現
#include <stdio.h>
#include <stdarg.h>
void myprintf(const char *format, ...)
{
va_list ap;
char c;
va_start(ap, format);
while(c = *format++)
{
switch(c)
{
case 'c':
{
char ch = va_arg(ap, int);
putchar(ch);
break;
}
case 's':
{
char *p = va_arg(ap, char *);
fputs(p, stdout);
break;
}
default:
putchar(c);
}
}
va_end(ap);
}
int main(void)
{
myprintf("c\ts\n", '1', "hello");
return 0;
}