c語言宏可變參數
⑴ c語言如何定義一個可變參數函數 如何調用其輸入的參數 分別
有專門的宏,處理可變參
void va_start( va_list arg_ptr, prev_param );
type va_arg( va_list arg_ptr, type );
void va_end( va_list arg_ptr );
一個簡單的例子
voidsimple_va_fun(inti,...)
{
va_listarg_ptr;
intj=0;
va_start(arg_ptr,i);
j=va_arg(arg_ptr,int);
va_end(arg_ptr);
printf("i=%dj=%dn",i,j);
return;
}
intmain()
{
simple_va_fun(1);
simple_va_fun(1,2);
simple_va_fun(1,200);
return0;
}
⑵ C語言中可變參數宏的va_start(ap, v)
我把你的提問分為3個問題:
1、為什麼printf("%s", ap);輸出不了?
2、va_start(ap, v)的定義中為什麼使用二級指針?
3、va_arg(ap,t) 的定義中為什麼用*(t *),它的作用是?
在解釋之前,先確認一個小問題:
在C語言中,指針這種類型的大小實際上一樣的,我的意思是說無論是char *a,還是int *a,或者是char **a,a這個指針變數所佔用的內存空間是一樣的(都是sizeof(a),究竟是等於4,還是8取決於CPU的位數)
先回答第一個問題:
你應該知道va_list的定義:typedef char * va_list;
也就是說ap可以理解為一個char *類型的變數,va_start(ap,c)這個執行之後,ap確實指向了可變參數列表中的第一個參數,注意【是ap這個指針指向了第一個參數】,而如果你的第一個參數是一個字元串(C語言中也就意味著是一個char*的變數),這樣的話,ap這個指針就指向了一個char*類型的指針變數,【指向指針的指針變數是二級指針變數】這個我就不用多說了吧,所以printf("%s", ap);是無法輸出的,而修改為printf("%s", *(char **)ap);應該就可以輸出了!
然後是第二個問題:
這里先說一下函數調用過程中參數傳遞的問題:
【 函數參數是以數據結構:棧的形式存取,從右至左入棧。
首先是參數的內存存放格式:參數存放在內存的堆棧段中,在執行函數的時候,從最後一個開始入棧。因此棧底高地址,棧頂低地址,舉個例子如下:
void func(int x, char *y, char z);
那麼,調用函數的時候,實參 char z 先進棧,然後是 char *y,最後是 int x,因此在內存中變數的存放次序是 x->y->z,因此,從理論上說,我們只要探測到任意一個變數的地址,並且知道其他變數的類型,通過指針移位運算,則總可以順藤摸瓜找到其他的輸入變數。】
注意,x,y,z這幾個變數是存放到堆棧中的,所以我們需要獲得的不是y這個變數本身,而是它在堆棧中的地址,而ap這個指針變數就是保存著堆棧中函數入參的地址的,所以在va_start(ap, v)的定義中要使用&v,而不管v變數本身是什麼類型的(哪怕v是一個指針變數,甚至是二級指針)&v都表示一個地址,所以可以強制轉換為va_list類型(也就是char *)。
第三個問題:
要睡覺了,先自己想吧,如果還不明白,就留言追問吧。
⑶ c語言中什麼是參數
如何寫可變參數的C函數以及這些可變參數的函數編譯器是如何實現的呢?下面是我為大家整理的關於c語言的參數介紹及使用,希望可以幫到大家哦。
簡單的可變參數的C函數
下面我們來探討如何寫一個簡單的可變參數的C函數.寫可變參數的C函數要在程序中用到以下這些宏:
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在這里是variable-argument(可變參數)的意思.
這些宏定義在stdarg.h中,所以用到可變參數的程序應該包含這個頭文件.下面我們寫一個簡單的可變參數的函數,改函數至少有一個整數參數,第二個參數也是整數,是可選的.函數只是列印這兩個參數的值.
void simple_va_fun(int i, ...)
{
va_list arg_ptr;
int j=0;
va_start(arg_ptr, i);
j=va_arg(arg_ptr, int);
va_end(arg_ptr);
printf(%d %dn, i, j);
return;
}
我們可以在我們的頭文件中這樣聲明我們的函數:
extern void simple_va_fun(int i, ...);
我們在程序中可以這樣調用:
simple_va_fun(100);
simple_va_fun(100,200);
從這個函數的實現可以看到,我們使用可變參數應該有以下步驟:
1)首先在函數里定義一個va_list型的變數,這里是arg_ptr,這個變數是指向參數的指針.
2)然後用va_start宏初始化變數arg_ptr,這個宏的第二個參數是第一個可變參數的前一個參數,是一個固定的參數.
3)然後用va_arg返回可變的參數,並賦值給整數j. va_arg的第二個參數是你要返回的參數的類型,這里是int型.
4)最後用va_end宏結束可變參數的獲取.然後你就可以在函數里使用第二個參數了.如果函數有多個可變參數的,依次調用va_arg獲取各個參數.
如果我們用下面三種 方法 調用的話,都是合法的,但結果卻不一樣:
1)simple_va_fun(100);
結果是:100 -123456789(會變的值)
2)simple_va_fun(100,200);
結果是:100 200
3)simple_va_fun(100,200,300);
結果是:100 200
我們看到第一種調用有錯誤,第二種調用正確,第三種調用盡管結果正確,但和我們函數最初的設計有沖突.下面一節我們探討出現這些結果的原因和可變參數在編譯器中是如何處理的.
可變參數在編譯器中的處理我們知道va_start,va_arg,va_end是在stdarg.h中被定義成宏的,由於1)硬體平台的不同 2)編譯器的不同,所以定義的宏也有所不同,下
面以VC++中stdarg.h里x86平台的宏定義摘錄如下(''號表示折行):
typedef char * va_list;
#define _INTSIZEOF(n)
((sizeof(n)+sizeof(int)-1)&~(sizeof(int) - 1) )
#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )
#define va_arg(ap,t)
( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
⑷ c語言如何封裝一個帶有可變參數的方法
需要借用C語言的VA_LIST宏定義,及相關操作來實現可變參數。
VA_LIST所在頭文件:#include <stdarg.h>,用法如下:
(1)首先在函數里定義一具VA_LIST型的變數,這個變數是指向參數的指針;
(2)然後用VA_START宏初始化剛定義的VA_LIST變數;
(3)然後用VA_ARG返回可變的參數,VA_ARG的第二個參數是你要返回的參數的類型(如果函數有多個可變參數的,依次調用VA_ARG獲取各個參數);
(4)最後用VA_END宏結束可變參數的獲取。
以下是一個自定義列印介面的實現:
intmy_printf(constchar*fmt,...)//...表示參數可變
{
va_listargs;//定義va_list
staticchargc_PrintfOutBuff[1000];
va_start(args,fmt);//初始化
vsnprintf((char*)gc_PrintfOutBuff,1000,(char*)fmt,args);//這里沒有使用VA_ARG取回單個變數,而是借用vsnprinf一次性讀取。
va_end(args);//結束獲取
puts("%s",(constchar*)gc_PrintfOutBuff);//使用。
return0;
}
⑸ C語言 可變參數宏的問題
這個問題可以這樣考慮:
你在write_log()函數里調用了vfprintf()函數,其實這個vfprintf()就是一個可以接受你從上層函數傳下來的可變參數串的函數。
你現在要在
log_info()
函數下調用
write_log()
函數,並想把可變參數串傳給它,你只要參考
vfprintf()
的函數定義來定義
write_log()
函數就可以。
c語言中
vfprintf()
函數的定義是:
int
vfprintf(file
*stream,
const
char
*format,
va_list
ap);
不知你是否能受到啟發。