0

我有一个关于该printf()方法如何打印有符号或无符号整数的问题。有一天,我发现自己在想,鉴于计算机没有十进制的概念,将二进制序列转换为人类可以理解的十进制数字序列是多么困难。

下面,我有一个printf()方法(从这里)及其相关方法。正如您在评论中看到的那样,我试图尽可能多地了解其printi()工作原理:

#define PAD_RIGHT 1
#define PAD_ZERO 2

#include <stdarg.h>

static void printchar(char **str, int c)
{
    extern int putchar(int c);

    if (str) {
        **str = c;
        ++(*str);
    }
    else (void)putchar(c);
}

static int prints(char **out, const char *string, int width, int pad)
{
    register int pc = 0, padchar = ' ';

    if (width > 0) {
        register int len = 0;
        register const char *ptr;
        for (ptr = string; *ptr; ++ptr) ++len;
        if (len >= width) width = 0;
        else width -= len;
        if (pad & PAD_ZERO) padchar = '0';
    }
    if (!(pad & PAD_RIGHT)) {
        for ( ; width > 0; --width) {
            printchar (out, padchar);
            ++pc;
        }
    }
    for ( ; *string ; ++string) {
        printchar (out, *string);
        ++pc;
    }
    for ( ; width > 0; --width) {
        printchar (out, padchar);
        ++pc;
    }

    return pc;
}

/* the following should be enough for 32 bit int */
#define PRINT_BUF_LEN 12

static int printi(char **out, int i, int b, int sg, int width, int pad, int letbase)
{
    /*
        i is the number we are turning into a string
        b is the base, i.e. base 10 for decimal
        sg is if the number is signed, i.e. 1 for signed (%d), 0 for unsigned (%u)

        By default, width and pad are 0, letbase is 97
    */

    char print_buf[PRINT_BUF_LEN];
    register char *s;
    register int t, neg = 0, pc = 0;
    register unsigned int u = i;

    if (i == 0)
    {
        print_buf[0] = '0';
        print_buf[1] = '\0';
        return prints(out, print_buf, width, pad);
    }

    if (sg && b == 10 && i < 0)
    {
        neg = 1;
        u = -i;
    }

    s = print_buf + PRINT_BUF_LEN - 1;
    *s = '\0';

    while (u)
    {
        t = u % b;

        if (t >= 10)
            t += letbase - '0' - 10;

        *--s = t + '0';
        u /= b;
    }

    if (neg)
    {
        if (width && (pad & PAD_ZERO))
        {
            printchar(out, '-');
            ++pc;
            --width;
        }
        else
            *--s = '-';
    }

    return pc + prints(out, s, width, pad);
}

static int print(char** out, const char* format, va_list args)
{
    register int width, pad;
    register int pc = 0;
    char scr[2];

    for (; *format != 0; ++format)
    {
        if (*format == '%')
        {
            ++format;
            width = pad = 0;

            if (*format == '\0')
                break;

            if (*format == '%')
                goto out;

            if (*format == '-')
            {
                ++format;
                pad = PAD_RIGHT;
            }

            while (*format == '0')
            {
                ++format;
                pad |= PAD_ZERO;
            }

            for (; *format >= '0' && *format <= '9'; ++format)
            {
                width *= 10;
                width += *format - '0';
            }

            if (*format == 's')
            {
                register char* s = (char*) va_arg(args, int);
                pc += prints(out, s ? s : "(null)", width, pad);
                continue;
            }

            if (*format == 'd')
            {
                pc += printi(out, va_arg(args, int), 10, 1, width, pad, 'a');
                continue;
            }

            if (*format == 'x')
            {
                pc += printi(out, va_arg(args, int), 16, 0, width, pad, 'a');
                continue;
            }

            if (*format == 'X')
            {
                pc += printi(out, va_arg(args, int), 16, 0, width, pad, 'A');
                continue;
            }

            if (*format == 'u')
            {
                pc += printi(out, va_arg(args, int), 10, 0, width, pad, 'a');
                continue;
            }

            if (*format == 'c')
            {
                /* char are converted to int then pushed on the stack */
                scr[0] = (char) va_arg(args, int);
                scr[1] = '\0';
                pc += prints(out, scr, width, pad);
                continue;
            }
        }
        else
        {
            out:
            printchar (out, *format);
            ++pc;
        }
    }

    if (out)
        **out = '\0';

    va_end(args);

    return pc;
}

int printf(const char *format, ...)
{
        va_list args;

        va_start( args, format );
        return print( 0, format, args );
}

如果我讨厌阅读库源代码的一件事,那就是它几乎不可读。具有一个字符且没有注释来解释它们的变量名称是一种痛苦。

您能否以简单的方式解释将整数转换为十进制数字字符串的方法到底是什么?

4

3 回答 3

2

您粘贴的代码不难阅读。我怀疑你可能早就放弃了。

暂时忽略负数的可能性,这个printi()例程:

  • 创建一个缓冲区以将数字打印到 12 个字符宽
  • 设置一个字符指针s指向该缓冲区的末尾** NULL-终止它,然后将指针移动一个字符到“左”

然后例程进入一个循环,只要数字保持 > 0

  • MOD除以10(即除以10取余数)
    • 这成为s指向的数字,因此将 ASCII 表示放在那里
    • s再次向左移动
  • 将数字设置为自身 / 10;这将删除刚刚打印的数字
  • 只要有更多数字要打印,就重复循环

这里唯一棘手的是处理负数,但是如果您了解负数是如何存储的,那一点也不棘手。

于 2012-12-14T12:07:02.093 回答
1

也许我一直盯着模板库标题太久了,但是那个库代码对我来说看起来很可读!

我将解释主循环,因为其余的(杂耍标志等)应该很容易弄清楚。

while (u)
{
    t = u % b;

    if (t >= 10)
        t += letbase - '0' - 10;

    *--s = t + '0';
    u /= b;
}

基本上我们正在做的是一次提取一个数字,从右到左。假设b == 10(即%dor的通常情况%u)。该%运算符称为模运算符,用于计算整数除法后的余数。该行第一次t = u % b;运行时,它计算输出字符串的最右边的数字——将数字u除以 10 后的余数。(假设数字u是 493:除以 10 后的余数是 3,即最右边的数字。)

在将这个最右边的数字提取到中后,如果它是 10 或更大tif语句决定如何“调用”这个数字。这个修正相当于进行了调整t,以便在下一行添加“0”(数字“0”的ASCII 值,即 48)时,结果将是一个以“a”或“A”开头的字母(到为大于 10 的基数生成十六进制数字和其他数字)。

之后的行将数字写入缓冲区。它进入缓冲区的最右边的字符print_buf(注意s早期如何初始化指向该缓冲区的末尾,而不是通常情况下的开始)。随后将指针s向左移动一个字符以准备下一个字符。

下一行 ,u /= b简单地除以u10,有效地丢弃了最右边的数字。(这是有效的,因为整数除法永远不会产生分数,并且总是向下舍入。)然后这会为下一个循环迭代打开第二个最右边的数字以进行处理。冲洗,重复。当没有任何东西时,循环最终停止(条件while (u)等同于条件while (u != 0))。

于 2012-12-14T12:05:45.340 回答
0

正整数 I 转换为以 10 为底的方法基本上是:

if (i == 0)
    printf("0");
else while (i != 0) {
    unsigned int j = i / 10;
    unsigned int digit = i - 10 * j;
    printf("%c", digit + '0');
    i = j;
}

除了这会向后打印出数字。

于 2012-12-14T12:04:28.697 回答