可变参数函数——printf模拟

我们先看看可变参数函数是怎样用的?

printf("%d",value);
printf("%s",str);
printf("%d %s",value,str);

printf函数的声明为:

int printf(char const* const _Format,...);
//其中三个点表示变参宏

可变参数函数的原理:
可变参数列表的实现是由stdarg.h中几个宏组成的:va_list,va_start(),va_arg(),va_end()
va_list:

typedef char* va_list;  //其实就是字符指针类型

va_start,va_arg,va_end

#define __crt_va_start_a(ap, v) ((void)(ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v)))
#define __crt_va_arg(ap, t)     (*(t*)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)))
#define __crt_va_end(ap)        ((void)(ap = (va_list)0))

其中_INTSIZEOF()定义为:

#define _INTSIZEOF(n)          ((sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1))
//以int所占字节数操作
//如果int占4字节,则以4字节对齐读取数据

_ADDRESSOF()定义为:

#define _ADDRESSOF(v) (&const_cast<char&>(reinterpret_cast<const volatile char&>(v)))
//目的是首先将传入的引用转化为char类型的引用,然后使用&取址

_ADDRESSOF()详见 STL实现细节之addressof()
我们先不考虑&操作符重载的情况,那么
va_start的宏定义为:

#define __crt_va_start_a(ap, v) ((void)(ap = (va_list)&v +  ((sizeof(v) + sizeof(int) - 1) & ~(sizeof(int) - 1))))
//ap指向可变参数的第一个参数位置处

va_arg宏定义中,(ap += _INTSIZEOF(t))使ap指向下一个参数的位置,(ap += _INTSIZEOF(t)) - _INTSIZEOF(t))则返回当前参数的位置。
va_end将ap指针设为空。

实现printf函数

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

void Myprint(const char* str, ...)
{
    double varglt = 0;
    int vargint = 0;
    char* vargpch = NULL;
    char vargch = 0;
    //char* pfmt = NULL;
    va_list vp=NULL;
    va_start(vp, str);

    while (*str) {
        if (*str == '%') {
            switch (*(++str))
            {
                case 'c':
                    vargch = va_arg(vp,char);
                    putchar(vargch);
                    break;
                case 'd':
                case 'i': {
                    vargint = va_arg(vp, int);
                    char strint[12] = { 0 };
                    int i = 0;
                    while (vargint != 0) {
                        strint[i] = vargint % 10;
                        vargint /= 10;
                        i++;
                    }
                    strint[i] = '\0';
                    for (int j = i - 1; j >= 0; j--) {
                        putchar(strint[j] + '0');
                    }
                    break;
                }
                case 'f': {
                    varglt = va_arg(vp, double);
                    char strdou[320] = { 0 };
                    int i = 0;
                    sprintf_s(strdou, "%f", varglt);
                    while (strdou[i]) {
                        putchar(strdou[i]);
                        i++;
                    }
                    break;
                }
                case 's':
                    vargpch = va_arg(vp, char*);
                    while (*vargpch)
                    {
                        putchar(*vargpch);
                        vargpch++;
                    }
                    break;
                default:
                    putchar(*(str - 1));
                    putchar(*str);
                    break;
            }
            str++;
        }
        else {
            putchar(*str);
            str++;
        }
    }
    va_end(vp);
}
int main() {
    float f = 1.001f;
    Myprint("%c %d %f\n", 'c','a',f);
    system("pause");
}

猜你喜欢

转载自blog.csdn.net/u013635579/article/details/75227082