可变参数的使用-printf简单实现

我们在写一个程序的时候,经常用到一些函数,例如printf函数,在我们用的时候觉得并没有什么觉得他很简单啊,我们使用的时候都没有注意过,它其实有很多种调用方法。

例如:


其实这就是可变参数,我们看看printf函数的原型,是这样的

 我们可以看到printf函数除了有一个固定的format参数以外,其他都是可变的,因此才有了不同的调用方法



        既然这个函数这么神奇,那么在私底下他到底是怎么实现的呢?接下来就这个问题,我们来一探究竟,同时希望可以帮助其他和我一样对此不了解的人,也让我加深对可变参数的理解,这里我们就模拟实现一下printf函数。


  在可变参数的模拟实现中,我们会用到以下这几个宏,这里我在msdn中搜到他们


va在这里是variable-argument(可变参数)的意思.这些宏定义在stdarg.h中,所以用到可变参数的程序应该包含这个头文件,接下来我们就来写一个简单函数模拟实现一下printf函数

#define _CRT_SECURE_NO_WARNINGS 1

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

void display(int ret)   //打印整数
{
	if(ret>9)
	{
		display(ret/10);
	}
		putchar(ret%10+'0');
}
void print(char *format, ...)  //模拟实现printf函数
{
	va_list arg;    //char *
	va_start(arg, format);
	while(*format != '\0')
	{
		int i = 0;
		if(*format == 's')    //出现s,就打印一个字符串
		{
			char* ret = NULL;
			ret = va_arg(arg, char*);
			while(*ret != '\0')
			{
			putchar(*ret);
			ret++;
			}
		}
		else if(*format == 'c')    //出现c,就打印一个字符
			putchar(va_arg(arg, char));
		else if(*format == 'd')    //出现d,就打印整型,这里没有打印负数
		{
			int ret = 0;
			ret = va_arg(arg, int);
			display(ret);
		}
		else                   //其他照常打印
			putchar(*format);
		    	format++;
	}
	va_end(arg);
}
int main()
{
	print("s ccc d%.\n","hello",'b','i','t',100);
	return 0;
}


接下来我们用画图的方式来分析

首先为MAIN函数开辟一块空间



然后在MAIN函数中调用printf函数,调用的时候有会进行传参


然后通过内存进行访问,模拟实现了printf函数

从这个函数的实现可以看到,我们使用可变参数应该有以下步骤: 


  1)首先在函数里定义一个va_list型的变量(依照个人情况),这里是arg,这个变量是指向参数的指针.
  2)然后用va_start宏初始化变量arg。
  3)然后用va_arg返回可变的参数,并赋值,va_arg的第二个参数是你要返回的参数的类型,这里是char型(返回字符串“hello”,字符‘b’,‘i’,‘t’),int型(返回100). 对应的返回值可以在程序中找到。
  4)最后用va_end宏结束可变参数的获取。


接下我们看看可变参数在编译器中的处理情况

typedef System::ArgIterator va_list;
#else
typedef char *  va_list;
#endif /* _M_CEE_PURE */
#define _VA_LIST_DEFINED
#endif

#ifdef  __cplusplus
#define _ADDRESSOF(v)   ( &reinterpret_cast<const char &>(v) )
#else
#define _ADDRESSOF(v)   ( &(v) )
#endif

#if     defined(_M_IA64) && !defined(_M_CEE_PURE)
#define _VA_ALIGN       8
#define _SLOTSIZEOF(t)   ( (sizeof(t) + _VA_ALIGN - 1) & ~(_VA_ALIGN - 1) )

#define _VA_STRUCT_ALIGN  16 

#define _ALIGNOF(ap) ((((ap)+_VA_STRUCT_ALIGN - 1) & ~(_VA_STRUCT_ALIGN -1)) \
        - (ap))
#define _APALIGN(t,ap)  (__alignof(t) > 8 ? _ALIGNOF((uintptr_t) ap) : 0)

#else
#define _SLOTSIZEOF(t)   (sizeof(t))
#define _APALIGN(t,ap)  (__alignof(t))
#endif

#if     defined(_M_CEE)

extern void __cdecl __va_start(va_list*, ...);
extern void * __cdecl __va_arg(va_list*, ...);
extern void __cdecl __va_end(va_list*);

#define _crt_va_start(ap,v)  ( __va_start(&ap, _ADDRESSOF(v), _SLOTSIZEOF(v), \
                                __alignof(v), _ADDRESSOF(v)) )
#define _crt_va_arg(ap,t)    ( *(t *)__va_arg(&ap, _SLOTSIZEOF(t), \
                                _APALIGN(t,ap), (t *)0) )
#define _crt_va_end(ap)      ( __va_end(&ap) )

#elif   defined(_M_IX86)

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

#define _crt_va_start(ap,v)  ( ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v) )
#define _crt_va_arg(ap,t)    ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
#define _crt_va_end(ap)      ( ap = (va_list)0 )

#elif defined(_M_IA64)

va_arg()取得类型的可变参数值

#define va_arg _crt_va_arg

#define _crt_va_arg(ap,t)    ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章