C语言的可变参数

1.原理与实现

C语言可变参数:

void func(char *fmt, ...);

C语言的函数参数占用大小是固定的,参数位置在一块内存中顺序排列,每一个参数占用int(正常32位操作系统4字节)大小,举个栗子:

void func(char c, char *str);

虽然c只占一个字节,但是它后面的3个字节是空着的,用来占位,接下来的就是第二个参数。。
参数占位

懂得这个原理后,用一个指针就可以遍历完成一个函数的所有参数了。

我们定义C语言的几个宏定义来表达这个想法:

//我们用来访问可变参数的指针类型(无符号指针使得编译器不会告警)。
typedef void * VA_list;  
//用来int字节对齐一个参数(不同操作系统的int占用字节数不同,在上个例子中这里就返回4字节)
#define INTSIZEOF(n) ((sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1)) 
//用来指向可变参数的头部位置,v是可变参数之前的最后一个固定参数 eg: void func(int n, char *fmt, ...);这里的fmt 就是v
#define VA_start(ap, v) (ap = (VA_list)&v + INTSIZEOF(v)) 
//关键步骤:按int字节递进ap指针,并且返回当前参数类型的参数
#define VA_arg(ap, type) (*(type *)((ap += INTSIZEOF(type))-INTSIZEOF (type))) 
//标定可变参数的末尾是空的,代表结束
#define VA_end(ap) ((void)0) 

使用这几个宏定义来实现可变参数函数

#include <stdio.h>

int my_print(const char* fmt, ...)
{
	VA_list ptr = NULL; 
	char c;
	VA_start(ptr, fmt);  //现在ptr指向fmt后面...的第一个参数
	while(*fmt != '\0')
	{
		c = *fmt++; 
		if(c !=	'%') {
			putchar(c);
		} else {
			switch(*fmt++)
			{
				case 's':
					printf("%s" ,VA_arg(ptr, char*)); //每使用一次VA_arg()就会递进一次ptr
					break; 
				case 'd':
					printf("%d", VA_arg(ptr, int)); 
					break; 
				case 'c':
					printf("%c", VA_arg(ptr, char));
					break; 
				case 'f':
					printf("%f", VA_arg(ptr, double)); 
					break; 
				default:
					putchar('\n');
					printf ("there is no such a type\n");
					break;
			}
		}
	}
	VA_end(ptr);
	return;
}

int main()
{
  my_print("hello %s %d", "world", 520);
}

2.调用库函数实现

C语言库 stdarg.h已经帮我们定义了这些宏定义,我们只要使用它就好了。把上面例子的 VA_xxx 改成小写的va_xxx 即可:

void my_print(const char *fmt, ...)
{
    va_list args;
    va_start(args, fmt);
    char c;
    while(*fmt != '\0')
	{
		c = *fmt++; 
		if(c !=	'%') {
			putchar(c);
		} else {
			switch(*fmt++)
			{
				case 's':
					printf("%s" ,va_arg(args, char*)); 
					break; 
				case 'd':
					printf("%d", va_arg(args, int)); 
					break; 
				case 'c':
					printf("%c", va_arg(args, char));
					break; 
				case 'f':
					printf("%f", va_arg(args, double)); 
					break; 
				default:
					break;
			}
		}
  }
  va_end(args);
  return;
}

3.可变参函数的封装

如果你正在使用一个可变参数函数,现在你想把该函数封装在一个自己的函数中调用,很不幸的是,你只能舍去原来函数的可变参,把可变参部分写入到固定参数里,因为可变参数没法沿用。
错误的栗子:

#include <stdio.h>
#include <stdarg.h>

void my_print(const gchar *format, ...){
    va_list args;
    va_start(args, format);
    printf(format,args);//错误,printf()函数只会将args看作是一个参数,占用一个参数位,并不会把args看作是多个可变参数。
    va_end(args);
}

正确的栗子:

#include <stdio.h>

void my_print(const gchar *format, ...){
    va_list args;
    char buf[1024];
    va_start(args, format);
    vsnprintf(buf, sizeof(buf), format, args); //将可变参数的所有字符都写入到buf中。
    va_end(args);
    
    printf("%s", buf); //直接打印buf.
    
}
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值