如何在c中实现自己的printf



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

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

这是我的原始代码,但太长了,我最多只能写25行。这就是为什么我想将%之后的字符传递给一个函数,该函数将打印存储在参数中的值

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

首先,应该将printf实现为对vfprintf()的调用,通过值传递va_list

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

这种方法被称为用continuations编译,其中每个函数调用下一个函数并返回其结果。如果编译器能够有效地处理这些尾部调用,那么具有许多参数的长格式字符串的堆栈深度可能会保持较小,否则它仍然在合理的限制范围内:每个转换规范有两个递归调用。

我怀疑这是42Epitech的人员对您的期望,但这是一种用小函数实现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 == '')
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 wordn");
ft_printf("%cello %sn", 'H', "word");
ft_printf("%d == 0%o == 0x%x == 0x%Xn", 1, 1, 1, 1);
ft_printf("%d == 0%o == 0x%x == 0x%Xn", 123, 123, 123, 123);
ft_printf("%d == 0%o == 0x%x == 0x%Xn", 0xdead, 0xdead, 0xdead, 0xdead);
return 0;
}

最新更新