您的位置:首页 > 运维架构 > Linux

linux可变参数 va_arg

2015-11-23 10:02 549 查看
在linux驱动中   kobject_add(struct kobject *kobj, struct kobject *parent,const char *fmt, ...)此时函数传入的可变参数;怎么处理?

   依据参数出入时是压栈操作,具体压栈顺序根据编译器规则来确定。
{
va_list args;
int retval;

if (!kobj)
return -EINVAL;

if (!kobj->state_initialized) {
printk(KERN_ERR "kobject '%s' (%p): tried to add an "
      "uninitialized object, something is seriously wrong.\n",
      kobject_name(kobj), kobj);
dump_stack();
return -EINVAL;
}
va_start(args, fmt);
retval = kobject_add_varg(kobj, parent, fmt, args);
va_end(args);

return retval;

}

/**

 * kobject_set_name_vargs - Set the name of an kobject

 * @kobj: struct kobject to set the name of

 * @fmt: format string used to build the name

 * @vargs: vargs to format the string.

 */

static int kobject_set_name_vargs(struct kobject *kobj, const char *fmt,
 va_list vargs)

{
const char *old_name = kobj->name;
char *s;

kobj->name = kvasprintf(GFP_KERNEL, fmt, vargs);
if (!kobj->name)
return -ENOMEM;

/* ewww... some of these buggers have '/' in the name ... */
while ((s = strchr(kobj->name, '/')))
s[0] = '!';

kfree(old_name);
return 0;

}

/* Simplified asprintf. */

char *kvasprintf(gfp_t gfp, const char *fmt, va_list ap)

{
unsigned int len;
char *p;
va_list aq;

va_copy(aq, ap);
len = vsnprintf(NULL, 0, fmt, aq);
va_end(aq);

p = kmalloc(len+1, gfp);
if (!p)
return NULL;

vsnprintf(p, len+1, fmt, ap);

return p;

}

对可变参数处理的核心部分

/**

 * vsnprintf - Format a string and place it in a buffer

 * @buf: The buffer to place the result into

 * @size: The size of the buffer, including the trailing null space

 * @fmt: The format string to use

 * @args: Arguments for the format string

 *

 * This function follows C99 vsnprintf, but has some extensions:

 * %pS output the name of a text symbol

 * %pF output the name of a function pointer

 * %pR output the address range in a struct resource

 *

 * The return value is the number of characters which would

 * be generated for the given input, excluding the trailing

 * '\0', as per ISO C99. If you want to have the exact

 * number of characters written into @buf as return value

 * (not including the trailing '\0'), use vscnprintf(). If the

 * return is greater than or equal to @size, the resulting

 * string is truncated.

 *

 * Call this function if you are already dealing with a va_list.

 * You probably want snprintf() instead.

 */

int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)

{
unsigned long long num;
int base;
char *str, *end, c;

int flags;
/* flags to number() */

int field_width;/* width of output field */
int precision;/* min. # of digits for integers; max
  number of chars for from string */
int qualifier;/* 'h', 'l', or 'L' for integer fields */
/* 'z' support added 23/7/1999 S.H.    */
/* 'z' changed to 'Z' --davidm 1/25/99 */
/* 't' added for ptrdiff_t */

/* Reject out-of-range values early.  Large positive sizes are
  used for unknown buffer sizes. */
if (unlikely((int) size < 0)) {
/* There can be only one.. */
static char warn = 1;
WARN_ON(warn);
warn = 0;
return 0;
}

str = buf;
end = buf + size;

/* Make sure end is always >= buf */
if (end < buf) {
end = ((void *)-1);
size = end - buf;
}

for (; *fmt ; ++fmt) {
if (*fmt != '%') {
if (str < end)
*str = *fmt;
++str;
continue;
}

/* process flags */
flags = 0;
repeat:
++fmt;
/* this also skips first '%' */
switch (*fmt) {
case '-': flags |= LEFT; goto repeat;
case '+': flags |= PLUS; goto repeat;
case ' ': flags |= SPACE; goto repeat;
case '#': flags |= SPECIAL; goto repeat;
case '0': flags |= ZEROPAD; goto repeat;
}

/* get field width */
field_width = -1;
if (isdigit(*fmt))
field_width = skip_atoi(&fmt);
else if (*fmt == '*') {
++fmt;
/* it's the next argument */
field_width = va_arg(args, int);
if (field_width < 0) {
field_width = -field_width;
flags |= LEFT;
}
}

/* get the precision */
precision = -1;
if (*fmt == '.') {
++fmt;

if (isdigit(*fmt))
precision = skip_atoi(&fmt);
else if (*fmt == '*') {
++fmt;
/* it's the next argument */
precision = va_arg(args, int);
}
if (precision < 0)
precision = 0;
}

/* get the conversion qualifier */
qualifier = -1;
if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' ||
   *fmt =='Z' || *fmt == 'z' || *fmt == 't') {
qualifier = *fmt;
++fmt;
if (qualifier == 'l' && *fmt == 'l') {
qualifier = 'L';
++fmt;
}
}

/* default base */
base = 10;

switch (*fmt) {
case 'c':
if (!(flags & LEFT)) {
while (--field_width > 0) {
if (str < end)
*str = ' ';
++str;
}
}
c = (unsigned char) va_arg(args, int);
if (str < end)
*str = c;
++str;
while (--field_width > 0) {
if (str < end)
*str = ' ';
++str;
}
continue;

case 's':
str = string(str, end, va_arg(args, char *), field_width, precision, flags);
continue;

case 'p':
str = pointer(fmt+1, str, end,
va_arg(args, void *),
field_width, precision, flags);
/* Skip all alphanumeric pointer suffixes */
while (isalnum(fmt[1]))
fmt++;
continue;

case 'n':
/* FIXME:
* What does C99 say about the overflow case here? */
if (qualifier == 'l') {
long * ip = va_arg(args, long *);
*ip = (str - buf);
} else if (qualifier == 'Z' || qualifier == 'z') {
size_t * ip = va_arg(args, size_t *);
*ip = (str - buf);
} else {
int * ip = va_arg(args, int *);
*ip = (str - buf);
}
continue;

case '%':
if (str < end)
*str = '%';
++str;
continue;

/* integer number formats - set up the flags and "break" */
case 'o':
base = 8;
break;

case 'x':
flags |= SMALL;
case 'X':
base = 16;
break;

case 'd':
case 'i':
flags |= SIGN;
case 'u':
break;

default:
if (str < end)
*str = '%';
++str;
if (*fmt) {
if (str < end)
*str = *fmt;
++str;
} else {
--fmt;
}
continue;
}
if (qualifier == 'L')
num = va_arg(args, long long);
else if (qualifier == 'l') {
num = va_arg(args, unsigned long);
if (flags & SIGN)
num = (signed long) num;
} else if (qualifier == 'Z' || qualifier == 'z') {
num = va_arg(args, size_t);
} else if (qualifier == 't') {
num = va_arg(args, ptrdiff_t);
} else if (qualifier == 'h') {
num = (unsigned short) va_arg(args, int);
if (flags & SIGN)
num = (signed short) num;
} else {
num = va_arg(args, unsigned int);
if (flags & SIGN)
num = (signed int) num;
}
str = number(str, end, num, base,
field_width, precision, flags);
}
if (size > 0) {
if (str < end)
*str = '\0';
else
end[-1] = '\0';
}
/* the trailing null byte doesn't count towards the total */
return str-buf;

}

EXPORT_SYMBOL(vsnprintf);

/**

 * vscnprintf - Format a string and place it in a buffer

 * @buf: The buffer to place the result into

 * @size: The size of the buffer, including the trailing null space

 * @fmt: The format string to use

 * @args: Arguments for the format string

 *

 * The return value is the number of characters which have been written into

 * the @buf not including the trailing '\0'. If @size is <= 0 the function

 * returns 0.

 *

 * Call this function if you are already dealing with a va_list.

 * You probably want scnprintf() instead.

 *

 * See the vsnprintf() documentation for format string extensions over C99.

 */

int vscnprintf(char *buf, size_t size, const char *fmt, va_list args)

{

int i;
i=vsnprintf(buf,size,fmt,args);
return (i >= size) ? (size - 1) : i;

}

         #define  _AUPBND                (sizeof (acpi_native_int) - 1)//4字节

        #define  _ADNBND                (sizeof (acpi_native_int) - 1)//4字节

        /*
 * Variable argument list macro definitions
       */
#define _bnd(X, bnd)            (((sizeof (X)) + (bnd)) & (~(bnd)))//bnd+1字节对其
#define va_arg(ap, T)           (*(T *)(((ap) += (_bnd (T, _AUPBND))) - (_bnd (T,_ADNBND))))
#define va_end(ap)              (void) 0
#define va_start(ap, A)         (void) ((ap) = (((char *) &(A)) + (_bnd (A,_AUPBND))))  

1、va_list

va_list表示可变参数列表类型,实际上就是一个char指针

2、va_start//4字节对其

va_start用于获取函数参数列表中可变参数的首指针(获取函数可变参数列表)

* 输出参数ap(类型为va_list):用于保存函数参数列表中可变参数的首指针(即,可变参数列表)

* 输入参数A:为函数参数列表中最后一个固定参数

3、va_arg

va_arg用于获取当前ap所指的可变参数并将并将ap指针移向下一可变参数

* 输入参数ap(类型为va_list):可变参数列表,指向当前正要处理的可变参数

* 输入参数T:正要处理的可变参数的类型

* 返回值: 当前可变参数的值

在C/C++中,默认调用方式_cdecl是由调用者管理参数入栈操作,且入栈顺序为从右至左,入栈方向为从高地址到低地址。因此,第1个到第n个参数被放在地址递增的堆栈里。所以,函数参数列表中最后一个固定参数的地址加上第一个可变参数对其的偏移量就是函数的可变参数列表了(va_start的实现);当前可变参数的地址加上下一可变参数对其的偏移量的就是下一可变参数的地址了(va_arg的实现)。这里提到的偏移量并不一定等于参数所占的字节数,而是为参数所占的字节数再扩展为机器字长(acpi_native_int)倍数后所占的字节数(因为入栈操作针对的是一个机器字),这也就是为什么_bnd那么定义的原因。

4、va_end

va_end用于结束对可变参数的处理。实际上,va_end被定义为空.它只是为实现与va_start配对(实现代码对称和"代码自注释"功能)

对可变参数列表的处理过程一般为:

1、用va_list定义一个可变参数列表

2、用va_start获取函数可变参数列表

3、用va_arg循环处理可变参数列表中的各个可变参数

4、用va_end结束对可变参数列表的处理
#include<stdio.h>

#include<stdarg.h>/*使用va_list、va_start等必须包含的头文件 */

#include<string.h>

/* linux C没有itoa函数,所以要自己写 */

char*itoa(inti,char*str)

{

intmod,div=fabs(i),index=0;

char*start,*end,temp;

do

{

mod=div%10;

str[index++]='0'+mod;

div=div/10;

}while(div!=0);

if(i<0)

str[index++]='-';

str[index]='\0';

for(start=str,end=str+strlen(str)-1;

start<end;start++,end--)

{

temp=*start;

*start=*end;

*end=temp;

}

returnstr;

}

voidprint(constchar*fmt,...)

{

charstr[100];

unsignedintlen,i,index;

intiTemp;

char*strTemp;

va_listargs;

va_start(args,fmt);

len=strlen(fmt);

for(i=0,index=0;i<len;i++)

{

if(fmt[i]!='%')/* 非格式化参数 */

{

str[index++]=fmt[i];

}

else/* 格式化参数 */

{

switch(fmt[i+1])

{

case'd':/* 整型 */

case'D':

iTemp=va_arg(args,int);

strTemp=itoa(iTemp,str+index);

index+=strlen(strTemp);

i++;

break;

case's':/* 字符串 */

case'S':

strTemp=va_arg(args,char*);

strcpy(str+index,strTemp);

index+=strlen(strTemp);

i++;

break;

default:

str[index++]=fmt[i];

}

}

}

str[index]='\0';

va_end(args);

printf(str);

}

intmain()

{

print("Version: %d; Modifier: %s\n",-958,"lingd");

return0;

}


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: