使用 write() 系统调用在 C 中实现 printf()

问题描述 投票:0回答:2

我不仅试图将输出返回到

stdout
,而且还试图返回将要测试的每种数据类型的大小。我正在使用
write()
系统调用将简单输出写入
stdout
(是的,我坚持使用
write()
,因为它在空间 + 时间复杂度方面更有效)在我的
else
声明中,但我不是确定每个变量转换的
case
子句的逻辑。此外,对我的代码组织的任何反馈以及任何反馈都将不胜感激。

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdarg.h>
#include <stdint.h>

char *convert(unsigned int, int);

int my_printf(char *format, ...)
{
    va_list args;
    va_start(args, format);
    unsigned int i;
    char *input;
    char *s;
    void *p;
    
    for (input = format; *input != '\0'; input++)
    {
        if (*input == '%') {
            input++;
            //FETCH & EXECUTE VARIABLE CONVERSION
            switch (*input)
            {
                case '%':   write(1, "%", 1);
                            break;

                case 'd':   i = va_arg(args, int);
                            if (i <= 0)
                            {
                                i = -i;
                                write(1, "-", 1);
                            }
                            write(1, convert(i, 10), strlen(convert(i, 10)));
                            break;
                
                case 'o':   i = va_arg(args, unsigned int);
                            write(1, convert(i, 8), strlen(convert(i, 8)));
                            break;
             

                case 'u':   i = va_arg(args, unsigned int);
                            write(1, convert(i, 10), strlen(convert(i, 10)));
                            break;
                

                case 'x':   i = va_arg(args, unsigned int);
                            write(1, convert(i, 16), strlen(convert(i, 16)));
                            break;
                

                case 'c':   i = va_arg(args, int);
                            write(1, &i, 1);
                            break;

                case 's':   s = va_arg(args, char *);
                            write(1, s, strlen(s));
                            break;

                case 'p':   p = va_arg(args, void*);
                            intptr_t ptr_val = (intptr_t)p;
                            write(1, convert(ptr_val, 16), strlen(convert(ptr_val, 16)));
                            break;

                default:    write(1, input - 1, 2);
                            break;
            }
        }
        else 
        {
                            write(1, input, 1);
        }
    }
    va_end(args);
    return 0;
}

char *convert(unsigned int n, int base)
{
    static char Rep[] = "0123456789ABCDEF";
    static char buffer[50];
    char *ptr;

    ptr = &buffer[49];
    *ptr = '\0';

    do
    {
        *--ptr = Rep[n % base];
        n /= base;
    }
    while (n != 0);

    return (ptr);
}

我尝试在每种情况下在

strlen()
内部使用
write()
,但我没有取回数据字节大小值,我认为它可以与
strlen()
一起使用。我也试过
size_t
sizeof
代替
strlen()
没有工作。

c printf size variadic-functions
2个回答
0
投票

如果您希望返回代码中正在测试的每种数据类型的大小,请使用

sizeof()
运算符来确定每个变量类型的大小。例如,要查找整数的大小,请使用
sizeof(int)
。 要检索输出字符串的长度,请使用
strlen()
但是,如果您希望返回每次转换的输出字符串的大小,您还必须包括您正在写入
stdout
的任何其他字符的大小,例如换行符或空格。考虑将转换逻辑分解为不同的函数,以更好地构建代码并使其更易于阅读和维护。


0
投票

[我]也在尝试返回将要测试的每种数据类型的大小

不清楚你的意思,但你应该返回输出的字符总数

stdout
.

请注意,直接为每个字符使用

write
系统调用和转换可能比使用
putchar()
并利用标准流函数执行的缓冲更有效。

你的代码有一些问题:

  • 对于格式字符串

    "%"
    :您输出一个
    %
    和一个空字节并继续迭代超出字符串的末尾,调用未定义的行为。

  • 转换数字两次是低效的。您应该存储

    convert
    返回的指针,并将其用作
    write
    strlen
    的参数。或者,
    convert
    可以返回生成的字符数以避免需要
    strlen()
    .

  • 对负数的支持不完整:

    INT_MIN
    将调用未定义的行为,因为计算
    -i
    会导致算术溢出。它产生一个负数,由
    convert
    计算的大多数数字都是负数,在使用负值索引
    Rep[n % base]
    时调用未定义的行为。

  • 无符号转换

    %u
    %o
    对于将被
    convert
    接收为负数的大值不正确。

  • format

    %c
    不适用于大端架构。您应该将字节值存储到
    char
    变量并将其传递给
    write
    .

  • format

    %p
    如果
    intptr_t
    是比
    int
    更大的类型并且不处理大指针值(负参数问题)。

© www.soinside.com 2019 - 2024. All rights reserved.