您的位置:首页 > 其它

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

2017-04-23 11:20 260 查看
我们在写一个程序的时候,经常用到一些函数,例如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)) )
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: