linux c 可变参数,linux c可变参数va_start、va_end、va_arg、va_list

linux c可变参数va_start、va_end、va_arg、va_list

首页 计算机相关 linux c基础 linux c可变参数va_start、va_end、va_arg、va_list

va_start、va_end、va_arg、va_list 其实是宏定义,在不定参数函数中会用到。

在解释上面几个之前,我们先来做几件事。

确定栈增长方向#include

#include

#include

#include

#include

#include

void test2();

void test1(){

int a = 1;

printf("a %p\n", &a);

test2();

}

void test2(){

int b = 1;

printf("b %p\n", &b);

}

int main(int argc, char *argv[]){

test1();

return 0;

}[root@izj6cfw9yi1iqoik31tqbgz c]# ./a.out

a 0x7ffc59d91b5c

b 0x7ffc59d91b3c

上面 test1 调用 test2,所以test1函数中的变量肯定比test2函数变量要先入栈,所以 变量a 先入栈。从输出的结果可以看出,a地址 > b地址。

得出结论先入栈的地址在高位,栈是向虚拟地址减少的方向增长(不同的环境可能不一样)。

确定函数参数入栈顺序#include

void test(int a, int b, char *aa, char *bb, char aaa,char bbb){

printf("%p %p %p %p %p %p\n", &a, &b, &aa, &bb, &aaa, &bbb);

}

int main(int argc, char *argv[]){

test(1,2,"沧浪水", "http://www.freecls.com",'a','b');

return 0;

}[root@izj6cfw9yi1iqoik31tqbgz c]# ./a.out

0x7ffc985f6abc 0x7ffc985f6ab8 0x7ffc985f6ab0 0x7ffc985f6aa8 0x7ffc985f6aa4 0x7ffc985f6aa0

从输出可以看出 参数a 地址最大,所以参数a先入栈,所以函数参数是从左往右依次入栈。

了解va_start、va_end...原理

从上文我们可以看出,函数参数是依次压入栈的,大小会根据实际类型,最小为int型(从上面可以看出,char型也占用4个字节)。所以要实现可变参数函数,我们必须知道以下3个条件:

1.最后一个已知参数的地址

2.未知参数的个数

3.每个未知参数的类型

就可以通过指针移动,分别取得各个参数。下面内核3.16 的相关宏定义,有兴趣的朋友可以自己去研究。#ifndef va_arg

#ifndef _VALIST

#define _VALIST

typedef char *va_list;

#endif /* _VALIST */

/* Storage alignment properties */

#define _AUPBND (sizeof (acpi_native_int) - 1)

#define _ADNBND (sizeof (acpi_native_int) - 1)

/* Variable argument list macro definitions */

#define _bnd(X, bnd) (((sizeof (X)) + (bnd)) & (~(bnd)))

#define va_arg(ap, T) (*(T *)(((ap) += (_bnd (T, _AUPBND))) - (_bnd (T,_ADNBND))))

#define va_end(ap) (ap = (va_list) NULL)

#define va_start(ap, A) (void) ((ap) = (((char *) &(A)) + (_bnd (A,_AUPBND))))

#endif /* va_arg */

va_start、va_end...用法

在了解了原理之后,我们通过例子来讲解实际用法。#include

#include

#include

#include

#include

#include

#include

#define BUF_SIZE 500

//未知类型,未知参数个数格式化

void errExit(int err_code, char *format, ...){

char userMsg[BUF_SIZE];

va_list argList;

//这里的第二个参数为最后一个已知参数

va_start(argList, format);

vsnprintf(userMsg, BUF_SIZE, format, argList);

va_end(argList);

printf("%s", userMsg);

exit(err_code);

}

//已知参数个数,已知类型

//第一个参数用来指定后面参数个数

//后面的参数类型全部为int

void dosum(int sum, ...){

va_list argList;

va_start(argList, sum);

int n = 0;

int i = 0;

for(; i < sum; i++){

n = n + va_arg(argList, int);

}

va_end(argList);

printf("sum:%d\n", n);

}

int main(int argc, char *argv[]){

dosum(4, 1,2,3,4);

errExit(1, "%s %s\n", "hello", "freecls");

}[root@izj6cfw9yi1iqoik31tqbgz c]# ./a.out

sum:10

hello freecls

读者只要记住一点,想要实现可变参数函数,必须满足上面提到的3个条件,当然实现方式各种各样。比如 vsnprintf() 它是通过传入格式化字符串来确定参数个数以及参数类型的(下面代表3个参数,类型分别为 int、char *、char)。"%d %s %c\n"

总结 - 通俗点讲//声明一个指针

va_list arglist;

//让arglist指向第一个未知参数,这里的xxx为最后一个已知参数

va_start (arglist, xxx)

//返回arglist地址,然后arglist往前移动sizeof(type)个字节(4字节对齐,即大小为4字节的倍数)

//其实就是返回当前参数,然后 arglist 指向下一个参数

va_arg(arglist, type)

//即arglist = NULL

va_end(arglist)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
va_startva_argva_end是C语言中用来传递可变数量参数的宏。它们通常与可变参数函数一起使用,例如printf函数。 首先,我们来介绍va_start宏。它的作用是初始化一个指向可变参数列表的指针,以便访问参数列表中的参数。具体语法如下: ```c void va_start(va_list ap, last_param); ``` 其中,ap是一个指向可变参数列表的指针,last_param是最后一个固定参数va_start宏会在last_param之后找到可变参数列表的起始位置,并将ap指向该位置。 接下来是va_arg宏,它用于获取可变参数列表中的参数值。具体语法如下: ```c type va_arg(va_list ap, type); ``` 其中,ap是一个指向可变参数列表的指针,type是要获取的参数的类型。va_arg宏会返回ap指向的参数值,并将ap移动到下一个参数的位置。 最后是va_end宏,它用于结束对可变参数列表的访问。具体语法如下: ```c void va_end(va_list ap); ``` 调用va_end宏后,ap指针将不再有效。 使用这三个宏,我们可以实现对可变数量参数的遍历和访问。例如,在printf函数中使用可变参数列表,可以通过va_startva_argva_end来依次获取不同类型的参数,并按照格式字符串进行输出。 需要注意的是,可变参数的传递是通过栈来实现的,所以对于不同的硬件平台和编译器,可变参数的传递方式可能有所不同。因此,在使用这些宏时,需要参考相关平台和编译器的文档进行正确的使用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值