首先,您应该将printf 实现为对vfprintf() 的调用,通过值传递va_list。
在您的 ft_vfprintf() 函数中,您可以通过函数指针数组将 va_list 按值传递给特定于每种格式的函数,但它们不会正确更新调用者中的 va_list 并且您不能可移植地传递一个指向va_list 的指针,因为这种类型可能被定义为一个数组。然而,这些函数可以将va_list 与其他信息一起以递归方式继续解析。
这种方法称为使用延续编译,其中每个函数调用下一个函数并返回其结果。如果编译器可以有效地处理这些尾调用,那么具有许多参数的长格式字符串的堆栈深度可能会保持较小,否则它仍然在合理的范围内:每个转换规范两次递归调用。
我怀疑这是 42 或 Epitech 的人们对您的期望,但这是一种用小功能实现 printf 的方法。但是请注意,完整的实现是非平凡的,因为格式规范还可能包含标志、修饰符以及宽度和精度参数,但它可能仍然可以使用额外的状态信息。
这是一个简单的例子:
#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;
}