简体   繁体   English

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

[英]How do I implement my own printf in c

I am recreating the C function printf , after the % the function goes to the if statement bellow, if I do not know the data type for the second argument how do I use va_arg() to access the arguments passed to my printf to print?我正在重新创建 C 函数printf ,在%函数转到下面的 if 语句之后,如果我不知道第二个参数的数据类型,我该如何使用va_arg()来访问传递给我的printf的参数进行打印?

if (str[a] == 'c' || str[a] == 'd' || str[a] == 's')
    print_char(str[a], va_arg(arg, ));

This is my original code, but it is too long, I have to make it 25 line at most.这是我的原始代码,但是太长了,我最多只能做到25行。 that is why I want to pass the character after % to a function that will then print the value stored in the argument这就是为什么我想将%之后的字符传递给一个函数,然后该函数将打印存储在参数中的值

void ft_printf(char *fmt, ...)
{
    char *s;
    unsigned int i;
    char *str;
    int a;

    va_list arg;
    va_start(arg, fmt);
    str = fmt;
    a = 0;
    while (str[a])
    {
        while (str[a] == ' ' || str[a] == '\t')
            a++;

        while (str[a] != '%')
        {
            ft_putchar(str[a]);
            a++;
        }
        a++;
        if (str[a] == 'c')
        {
            i = va_arg(arg, int);
            ft_putchar(i);
        }
        else if (str[a] == 'd')
        {
            i = va_arg(arg, int);
            ft_putchar(i);
        }
        else if (str[a] == 's')
        {
            s = va_arg(arg, char *);
            ft_putstr(s);
        }
        a++;
    }
    va_end(arg);
}

First, you should implement printf as a call to vfprintf() , passing the va_list by value.首先,您应该将printf实现为对vfprintf()的调用,按值传递va_list

In your ft_vfprintf() function, you could pass the va_list by value to a function specific for each format, via an array of function pointers, but they would not update the va_list in the caller correctly and you cannot portably pass a pointer to the va_list as this type might be defined as an array.在您的ft_vfprintf()函数中,您可以通过函数指针数组将va_list按值传递给特定于每种格式的函数,但它们不会正确更新调用者中的va_list并且您无法将指针可移植地传递给va_list因为这种类型可能被定义为一个数组。 Yet these functions can take the va_list along with other information to continue the parsing recursively.然而,这些函数可以将va_list与其他信息一起递归地继续解析。

This approach is called compiling with continuations , where each function calls the next one and returns its result.这种方法称为使用 continuation 进行编译,其中每个函数调用下一个函数并返回其结果。 If the compiler can handle these tail calls efficiently, the stack depth for long format strings with many arguments might stay small, otherwise it is still within reasonable limits: two recursive call for each conversion specification.如果编译器可以有效地处理这些尾调用,则具有许多参数的长格式字符串的堆栈深度可能会保持很小,否则它仍然在合理的范围内:每个转换规范两次递归调用。

I doubt this is what the folks at 42 or Epitech expect from you, but it is a way to implement printf with small functions.我怀疑这是否是42Epitech的人们对您的期望,但这是一种使用小函数实现printf方法。 Note however that a full implementation is non-trivial as the format specifications may also contain flags, modifiers and width and precision arguments, but it might still be feasible with extra state information.但是请注意,完整的实现并非易事,因为格式规范还可能包含标志、修饰符以及宽度和精度参数,但使用额外的状态信息可能仍然可行。

Here is a simplistic example:这是一个简单的例子:

#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;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM