《C语言接口与实现》实验——可变参数表的使用(va_list, va_start, va_arg, va_end)

《C语言接口与实现》作为接口库,源文件中大量使用了可变参数表,这些到底是怎么使用的?先来看这几个例子,基本明白了可变参数表使用。后面部分从网上整理了原理:

源程序:

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

//
// 使用示例1:追加串
// 
void Va_Fn1(char *dest, char *data, ...)
{
	va_list ap;
	char *p = data;	//指向第一个可变参数

	//第二个参数就是写 ... 前面那个
	va_start(ap, data);

	//遍历每一个可变参数,取出来使用
	while(1)
	{	
		//访问当前这个可变参数,先用,后遍历!!
		strcat(dest, p);

		p = va_arg(ap, char *);
		if (p == NULL) break;
	}


	//结束
	va_end(ap);
}

//
// 使用示例2:累加和
// 
int Va_Fn2(int a, ...)
{
	int ret = a;	//指向第一个参数
	int sum = 0;

	va_list ap;

	//第二个参数就是写 ... 前面那个
	va_start(ap, a);
	
	//遍历每一个可变参数,取出来使用
	while(1)
	{
		//先用,后遍历
		sum += ret;


		ret =va_arg(ap, int);
		if (ret == -1) break;
	}

	//结束
	va_end(ap);

	return sum;
}



//
// 使用示例3:使用数据结构
// 
typedef struct
{
	int x;
	int y;
}MY_TYPE;
void Va_Fn3(int n, MY_TYPE *p, ...)
{
	int i = 0;
	va_list ap;
	MY_TYPE *tmp = p;

	//第二个参数就是写 ... 前面那个
	va_start(ap, p);
	
	for(; i<n; i++)
	{
		printf("\t%d-----%d\n", tmp->x, tmp->y);

		tmp = va_arg(ap, MY_TYPE *);
	}
	

	//结束
	va_end(ap);


}


//
// 使用示例4:稍复杂的可变参数表
//		(char *, int, int),		(char *, int, int), ......	
// 
void Va_Fn4(char *msg, ...)
{
	va_list ap;
	int i, j;
	char *str = msg;	//指向第一个参数
	
	//第二个参数就是写 ... 前面那个
	va_start(ap, msg);

	while(1)
	{
		//使用可变参数表,先使用
		i = va_arg(ap, int);
		j = va_arg(ap, int);
		printf("\t%s---%d----%d\n", str, i, j);

		//后遍历
		str = va_arg(ap, char *);	//第二个参数是【可变参数】的类型
		if (str == NULL) break;
	}

	//结束
	va_end(ap);

}


void main()
{
	//示例1
	char dest[1000] = {0};
	Va_Fn1(dest, "Hello ", "OK ", "欢迎 ", "Yes ", NULL);
	printf("示例1 = %s\n", dest);


	//示例2
	int x = Va_Fn2(9, 9, 1, 3, 90, -1);
	printf("示例2 = %d\n", x);

	//示例3
	MY_TYPE a, b, c;
	a.x = 100;
	a.y = 300;
	b.x = 1100;
	b.y = 1300;
	c.x = 6100;
	c.y = 6300;
	printf("示例3:\n");
	Va_Fn3(3, &a, &b, &c);

	//示例4
	printf("示例4:\n");
	Va_Fn4("Hello", 1, 2, "XYZ", 300, 600, "ABC", 77, 88, NULL);
}

输出:

示例1 = Hello OK 欢迎 Yes
示例2 = 112
示例3:
        100-----300
        1100-----1300
        6100-----6300
示例4:
        Hello---1----2
        XYZ---300----600
        ABC---77----88
Press any key to continue

原理:

1. 函数参数是以数据结构:栈的形式存取,从右至左入栈

2. 首先是参数的内存存放格式:参数存放在内存的堆栈段中,在执行函数的时候,从最后一个开始入栈。因此栈底高地址,栈顶低地址,举个例子如下:

void func(int x, float y, charz);
那么,调用函数的时候,实参char z 先进栈,然后是 float y,最后是 intx,因此在内存中变量的存放次序是 x->y->z,因此,从理论上说,我们只要探测到任意一个变量的地址,并且知道其他变量的类型,通过指针移位运算,则总可以顺藤摸瓜找到其他的输入变量。<----这就是原理!!

3. 看源码(vc98/include/stdarg.h):(注意是X86相关的,不是mips,不是ALPHA的,不是PPC等等的!)

typedef char *  va_list;


#ifdef  _M_IX86




#define _INTSIZEOF(n)   ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )


#define va_start(ap,v)  ( ap = (va_list)&v + _INTSIZEOF(v) )
#define va_arg(ap,t)    ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
#define va_end(ap)      ( ap = (va_list)0 )


#elif   defined(_M_MRX000)

这里有复杂的宏,展开它:在“工程属性” —〉“C/C++”—〉“Project Options” 手工填入/P,然后rebuild,会产生于.cpp同名的.i文件,里面的宏被展开了。来看展开后的第一个函数:

void Va_Fn1(char *dest, char *data, ...)
{
	va_list ap;
	char *p = data;	

	
	( ap = (va_list)&data + ( (sizeof(data) + sizeof(int) - 1) & ~(sizeof(int) - 1) ) );

	
	while(1)
	{	
		
		strcat(dest, p);

		p = ( *(char * *)((ap += ( (sizeof(char *) + sizeof(int) - 1) & ~(sizeof(int) - 1) )) - ( (sizeof(char *) + sizeof(int) - 1) & ~(sizeof(int) - 1) )) );
		if (p == 0) break;
	}


	
	( ap = (va_list)0 );
}

再看展开的第二个函数,这个较简单:

int Va_Fn2(int a, ...)
{
	int ret = a;	
	int sum = 0;

	va_list ap;

	
	( ap = (va_list)&a + ( (sizeof(a) + sizeof(int) - 1) & ~(sizeof(int) - 1) ) );
	
	
	while(1)
	{
		
		sum += ret;


		ret =( *(int *)((ap += ( (sizeof(int) + sizeof(int) - 1) & ~(sizeof(int) - 1) )) - ( (sizeof(int) + sizeof(int) - 1) & ~(sizeof(int) - 1) )) );
		if (ret == -1) break;
	}

	
	( ap = (va_list)0 );

	return sum;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
通过va_listva_start、va_argva_end宏是C语言中用于处理可变参数的一组宏。它们通常用于函数中,当函数需要接受不定数量的参数时,可以使用这些宏来获取和处理这些参数。 具体介绍如下: 1. va_listva_list是一个类型,用于声明一个指向参数列的指针。它在函数中用于存储可变参数的信息。 2. va_start:va_start宏用于初始化va_list指针。它接受两个参数,第一个参数是一个va_list类型的变量,第二个参数是可变参数中最后一个已知的固定参数的名称。通过调用va_start宏,可以将va_list指针指向可变参数中的第一个可变参数。 3. va_argva_arg宏用于获取可变参数中的下一个参数的值。它接受两个参数,第一个参数是va_list类型的变量,第二个参数是要获取的参数的类型。通过调用va_arg宏,可以依次获取可变参数中的每个参数的值,并且每次调用后,va_list指针会自动指向下一个参数。 4. va_endva_end宏用于结束对可变参数的访问。它接受一个参数,即va_list类型的变量。通过调用va_end宏,可以释放与可变参数相关的资源。 使用这些宏的一般步骤如下: 1. 在函数中声明一个va_list类型的变量。 2. 调用va_start宏,将va_list指针指向可变参数中的第一个可变参数。 3. 使用va_arg宏依次获取可变参数中的每个参数的值。 4. 调用va_end宏,结束对可变参数的访问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值