我如何在C中实现自己的printf

时间:2018-08-04 10:58:52

标签: c

我正在重新创建C函数printf,在%之后,该函数转到if语句下面,如果我不知道第二个参数的数据类型如何使用{{1 }}来访问传递给我的va_arg()的参数以进行打印?

printf

这是我的原始代码,但是太长了,我最多只能将其设置为25行。这就是为什么我想将if (str[a] == 'c' || str[a] == 'd' || str[a] == 's') print_char(str[a], va_arg(arg, )); 之后的字符传递给一个函数,该函数随后将打印存储在参数

中的值
%

1 个答案:

答案 0 :(得分:2)

首先,您应该实现printf作为对vfprintf()的调用,并按值传递va_list

ft_vfprintf()函数中,可以通过函数指针数组将va_list按值传递给特定于每种格式的函数,但是它们不会更新va_list中的正确地调用方,并且您不能将指针便携地传递到va_list,因为此类型可能被定义为数组。然而,这些功能可以将va_list连同其他信息一起继续递归地进行解析。

这种方法称为连续编译,其中每个函数调用下一个函数并返回其结果。如果编译器可以有效地处理这些尾部调用,则带有许多参数的长格式字符串的堆栈深度可能会很小,否则仍在合理的范围内:每个转换规范两次递归调用。

我怀疑这是 42 Epitech 的人们对您的期望,但这是一种实现printf且功能较小的方法。但是请注意,完整的实现非常简单,因为格式规范可能还包含标志,修饰符以及width和precision参数,但是使用其他状态信息仍然可能可行。

这是一个简单的例子:

#include <stdarg.h>
#include <unistd.h>

int ft_putchar(int c) {
    char a[1];
    a[0] = (char)c;
    return write(0, a, 1);
}

static int ft_printf_aux(const char *fmt, va_list ap, int len);

static int ft_print_c(const char *fmt, va_list ap, int len) {
    int c = va_arg(ap, int);
    ft_putchar(c);
    return ft_printf_aux(fmt, ap, len + 1);
}

static int ft_putnum(unsigned long long n, unsigned int base, const char *digits) {
    int res = 1;
    if (n >= base)
        res += ft_putnum(n / base, base, digits);
    ft_putchar(digits[n % base]);
    return res;
}

static int ft_print_d(const char *fmt, va_list ap, int len) {
    int n = va_arg(ap, int);
    unsigned long long u;
    if (n < 0) {
        ft_putchar('-');
        len++;
        u = -(unsigned)n;
    } else {
        u = n;
    }
    len += ft_putnum(u, 10, "0123456789");
    return ft_printf_aux(fmt, ap, len);
}

static int ft_print_o(const char *fmt, va_list ap, int len) {
    unsigned int n = va_arg(ap, unsigned int);
    len += ft_putnum(n, 8, "01234567");
    return ft_printf_aux(fmt, ap, len);
}

static int ft_print_u(const char *fmt, va_list ap, int len) {
    unsigned int n = va_arg(ap, unsigned int);
    len += ft_putnum(n, 10, "0123456789");
    return ft_printf_aux(fmt, ap, len);
}

static int ft_print_x(const char *fmt, va_list ap, int len) {
    unsigned int n = va_arg(ap, unsigned int);
    len += ft_putnum(n, 16, "0123456789abcdef");
    return ft_printf_aux(fmt, ap, len);
}

static int ft_print_X(const char *fmt, va_list ap, int len) {
    unsigned int n = va_arg(ap, unsigned int);
    len += ft_putnum(n, 16, "0123456789ABCDEF");
    return ft_printf_aux(fmt, ap, len);
}

static int ft_print_s(const char *fmt, va_list ap, int len) {
    const char *s = va_arg(ap, const char *);
    if (s == NULL) {
        s = "(null)";
    }
    while (*s) {
        ft_putchar(*s++);
        len++;
    }
    return ft_printf_aux(fmt, ap, len);
}

typedef int (*ft_print_dispatch_f)(const char *fmt, va_list ap, int len);

static ft_print_dispatch_f const ft_print_dispatch[256] = {
    ['c'] = ft_print_c,
    ['d'] = ft_print_d,
    ['i'] = ft_print_d,
    ['o'] = ft_print_o,
    ['u'] = ft_print_u,
    ['x'] = ft_print_x,
    ['X'] = ft_print_X,
    ['s'] = ft_print_s,
};

static int ft_printf_aux(const char *fmt, va_list ap, int len) {
    int c;

    while (*fmt) {
        c = (unsigned char)*fmt++;
        if (c != '%') {
            ft_putchar(c);
            len++;
        } else {
            c = (unsigned char)*fmt++;
            if (ft_print_dispatch[c] == NULL) {
                if (c == '\0')
                    break;
                ft_putchar(c);
                len++;
            } else {
                return ft_print_dispatch[c](fmt, ap, len);
            }
        }
    }
    return len;
}

int ft_vprintf(const char *fmt, va_list ap) {
    return ft_printf_aux(fmt, ap, 0);
}

int ft_printf(const char *fmt, ...) {
    va_list ap;
    int n;
    va_start(ap, fmt);
    n = ft_printf_aux(fmt, ap, 0);
    va_end(ap);
    return n;
}

int main(void) {
    ft_printf("Hello word\n");
    ft_printf("%cello %s\n", 'H', "word");
    ft_printf("%d == 0%o == 0x%x == 0x%X\n", 1, 1, 1, 1);
    ft_printf("%d == 0%o == 0x%x == 0x%X\n", 123, 123, 123, 123);
    ft_printf("%d == 0%o == 0x%x == 0x%X\n", 0xdead, 0xdead, 0xdead, 0xdead);
    return 0;
}