天天看點

編寫可變參數函數 c語言,C語言可變參數函數的編寫

1. 引言

C語言我們接觸的第一個庫函數是

printf(“hello,world!”);其參數個數為1個。

然後,我們會接觸到諸如:

printf(“a=%d,b=%s,c=%c”,a,b,c);此時其參數個數為4個。

在linux下,輸入man 3 printf,可以看到prinf函數原型如下:

SYNOPSIS

#include

int printf(const char *format, ...);

後面的三個點...表示printf參數個數是不定的.

如何實作可變參數函數?

2. 編寫可變函數的素材準備

為了編寫可變參數函數,我們需要用到頭檔案下定義的以下函數:

void va_start(va_list ap, last);

type va_arg(va_list ap, type);

void va_end(va_list ap);

void va_copy(va_list dest, va_list src);

其中:

va_list是用于存放參數清單的資料結構。

va_start函數根據初始化last來初始化參數清單。

va_arg函數用于從參數清單中取出一個參數,參數類型由type指定。

va_copy函數用于複制參數清單。

va_end函數執行清理參數清單的工作。

函數官方說明,如果你看到英文就煩,可以自行忽略以下說明。

va_start()

The va_start() macro initializes ap for subsequent use by va_arg() and

va_end(), and must be called first.

The argument last is the name of the last argument before the variable

argument list, that is, the last argument of which the calling function

knows the type.

Because the address of this argument may be used in the va_start()

macro, it should not be declared as a register variable, or as a func‐

tion or an array type.

va_arg()

The va_arg() macro expands to an expression that has the type and value

of the next argument in the call. The argument ap is the va_list ap

initialized by va_start(). Each call to va_arg() modifies ap so that

the next call returns the next argument. The argument type is a type

name specified so that the type of a pointer to an object that has the

specified type can be obtained simply by adding a * to type.

The first use of the va_arg() macro after that of the va_start() macro

returns the argument after last. Successive invocations return the

values of the remaining arguments.

If there is no next argument, or if type is not compatible with the

type of the actual next argument (as promoted according to the default

argument promotions), random errors will occur.

If ap is passed to a function that uses va_arg(ap,type) then the value

of ap is undefined after the return of that function.

va_end()

Each invocation of va_start() must be matched by a corresponding invo‐

cation of va_end() in the same function. After the call va_end(ap) the

variable ap is undefined. Multiple traversals of the list, each brack‐

eted by va_start() and va_end() are possible. va_end() may be a macro

or a function.

GNU給出的一個執行個體:

#include

#include

void

foo(char *fmt, ...)

{

va_list ap;

int d;

char c, *s;

va_start(ap, fmt);

while (*fmt)

switch (*fmt++) {

case 's':

s = va_arg(ap, char *);

printf("string %s\n", s);

break;

case 'd':

d = va_arg(ap, int);

printf("int %d\n", d);

break;

case 'c':

c = (char) va_arg(ap, int);

printf("char %c\n", c);

break;

}

va_end(ap);

}

說明:

va_start(ap, fmt);用于根據fmt初始化可變參數清單。

va_arg(ap, char *);用于從參數清單中取出一個參數,其中的char *用于指定所取的參數的類型為字元串。每次調用va_arg後,參數清單ap都會被更改,以使得下次調用時能得到下一個參數。

va_end(ap);用于對參數清單進行一些清理工作。調用完va_end後,ap便不再有效。

以上程式給了我們一個實作printf函數的是思路,即:通過調用va_start函數,來得到參數清單,然後我們一個個取出參數來進行輸出即可。

例如:對于printf(“a=%d,b=%s,c=%c”,a,b,c)語句;fmt的值為a=%d,b=%s,c=%c,調用va_start函數将參數a,b,c存入了ap中。注意到:fmt中的%為特殊字元,緊跟%後的參數指明了參數類型.

是以我們的簡易printf函數如下:

#include

#include

void

myprintf(char *fmt, ...)

{

va_list ap;

int d;

double f;

char c;

char *s;

char flag;

va_start(ap,fmt);

while (*fmt){

flag=*fmt++;

if(flag!='%'){

putchar(flag);

continue;

}

flag=*fmt++;//記得後移一位

switch (flag)

{

case 's':

s=va_arg(ap,char*);

printf("%s",s);

break;

case 'd':

d = va_arg(ap, int);

printf("%d", d);

break;

case 'f':

d = va_arg(ap,double);

printf("%d", d);

break;

case 'c':

c = (char)va_arg(ap,int);

printf("%c", c);

break;

default:

putchar(flag);

break;

}

}

va_end(ap);

}

int main(){

char str[10]="linuxcode";

int i=1024;

double f=3.1415926;

char c='V';

myprintf("string is:%s,int is:%d,double is:%f,char is :%c",str,i,f,c);

}

從上面我們可以知道可變參數函數的編寫,必須要傳入一個參數fmt,用來告訴我們的函數怎樣去确定參數的個數。我們的可變參數函數是通過自己解析這個參數來确定函數參數個數的。

比如,我們編寫一個求和函數,其函數實作如下:

int sum(int cnt,...){

int sum=0;

int i;

va_list ap;

va_start(ap,cnt);

for(i=0;i

sum+=va_arg(ap,int);

va_end(ap);

return sum;

}

簡單吧,總結一下就是:通過va_start初始化參數清單(也就能得到具體的參數個數了),然後使用va_arg函數從參數清單中取出你想要的參數,最後調用va_end執行清理工作。

這一篇就介紹到這來,另外一種變參函數的實作方法,比如說以下函數,他們是怎麼實作的?

int execl(const char *path, const char *arg, ...);

int execlp(const char *file, const char *arg, ...);

int execle(const char *path, const char *arg,

..., char * const envp[]);

作者:Viidiot  微信公衆号:linux-code

轉載請保留作者、連結。