0%

可变参数的函数

如何声明一个可变参数的函数

在无法给出所有传递给函数的参数的类型和数目时,可以使用省略号(…)指定函数参数表。有如下几种形式:

1
2
3
void fun1(int a, double b, ...); //给出确定的几个参数,其他用省略号
void fun2(int a ...); //省略号前有或者没有逗号都是可以的
void fun3(...); //也可以不确定任何参数,但和没有参数是不一样的

举个例子:计算n个整数的和。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
- (int)test_n_number_add:(int)n, ... {
int res = 0;

va_list ap; //声明
va_start(ap, n); //绑定
while (n > 0) {
res += va_arg(ap, int); //获取参数。va_arg返回的就是参数的值。
n -= 1;
}
va_end(ap); //释放
NSLog(@"res:%d", res);

return res;
}

va_list存在的两个隐患:

1.如何确定参数的类型。

没有其他办法,就是通过强制转换。va_arg就是把当前指针所指向的内容强制转换到指定类型。像printf函数还有个格式化参数来说明后面参数的类型。如果是自己写的,同样得事先约定好一套规则。

2.如何确定参数的个数。也就是参数结束标志。

也没有规定,同样得事先约定好一套规则。比如这里第一个固定参数说明了后续可变参数的个数。再比如求自然数的平方和,那么可以把负数和0作为它的结束标志。其他系统函数如scanf把接收到的回车符作为结束标志,大家熟知的printf对字符串的处理用’\0’作为结束标志。

由于上面的原因,对于可变参数的函数,外部调用者和实现者必须遵守共同的约定才行。可变参数的函数,一般在格式化函数中用的比较多。

NSLog 和 NSLogv

1
2
FOUNDATION_EXPORT void NSLog(NSString *format, ...) NS_FORMAT_FUNCTION(1,2) NS_NO_TAIL_CALL;
FOUNDATION_EXPORT void NSLogv(NSString *format, va_list args) NS_FORMAT_FUNCTION(1,0) NS_NO_TAIL_CALL;

NSLogv可以传入一个可变参数列表,而NSLog不能(废话)。

考虑这样一种情况:函数A接受可变参数,函数B也接受可变参数。A内部要调用函数B,那么怎么把可变参数传给B呢?那么就可以使用va_list先获取到可变参数列表,再传给B。

举例:

1
2
3
4
5
6
7
8
9
10
11
12
13
- (void)test_nslog {
NSString *format = @"this is object:%@, age = %d, ch = %c";
NSLog(format, self, age, 's');
}

- (void)test_nslogv:(NSString *)format, ... {
NSString *myLogFormat = [NSString stringWithFormat:@"[my log v0]:%@", format];

va_list ap;
va_start(ap, format);
NSLogv(myLogFormat, ap); //获取到参数列表后,直接传给NSLogv。因为我们并不关心参数列表里究竟是啥参数。
va_end(ap);
}

参考

理解可变参数va_list、va_start、va_arg、va_end原理及使用方法

觉得文章有帮助可以打赏一下哦!