我有以下函数:第一个函数打印给定基数(基数)中的无符号整数。
第二个函数的作用完全相同,但使用有符号整数。
如您所见,这些函数的主体完全相同。为了防止同一代码加倍,我已经挣扎了几个小时,但我找不到解决这个问题的方法。
无符号函数:

const char *digit = "0123456789abcdef";
int print_int_helper_unsigned(unsigned int n, int radix, const char *digit) {

  int result;

  if (n < radix) {

    putchar(digit[n]);

    return 1;
  }
  else {
    result = print_int_helper_unsigned(n / radix, radix, digit);

          putchar(digit[n % radix]);

    return 1 + result;
  }
}

有符号函数:
int print_int_helper( int n, int radix, const char *digit) {

  int result;

  if (n < radix) {

    putchar(digit[n]);

    return 1;
  }
  else {
    result = print_int_helper(n / radix, radix, digit);

          putchar(digit[n % radix]);

    return 1 + result;
  }
}

最佳答案

将递归部分重构为帮助函数:

static void fprintf_digits_recursive(FILE *out,
                                     const unsigned long radix,
                                     const char *digits,
                                     unsigned long value)
{
    if (value >= radix)
        fprintf_digits_recursive(out, radix, digits, value / radix);
    fputc(digits[value % radix], out);
}

我将其标记为static,因为它只应在此编译单元(文件)中可见,而不应在外部直接调用。
helper函数的目的是打印出一个数字。如果value有多个数字,则首先打印较高的数字。(这就是为什么fputc()在递归调用之后。)
有符号和无符号整数打印机使用帮助程序,因此:
void print_int(const int value, const char *digits, const int radix)
{
    if (radix < 1 || !digits) {
        fprintf(stderr, "print_int(): Invalid radix and/or digits!\n");
        exit(EXIT_FAILURE);
    }

    if (value < 0) {
        fputc('-', stdout);
        fprintf_digits_recursive(stdout, radix, digits, (unsigned long)(-value));
    } else
        fprintf_digits_recursive(stdout, radix, digits, (unsigned long)(value));
}

void print_uint(const unsigned int value, const char *digits, const int radix)
{
    if (radix < 1 || !digits) {
        fprintf(stderr, "print_int(): Invalid radix and/or digits!\n");
        exit(EXIT_FAILURE);
    }

    fprintf_digits_recursive(stdout, radix, digits, (unsigned long)value);
}

我特意添加了输出流标识符并更改了参数顺序,以便更容易理解可视函数(有时也称为包装函数,如果它们非常简单的话)与实际执行实际工作的内部助手函数有何不同。
我将基数和数字检查添加到包装器函数中,因为这是推荐的做法。(也就是说,不要将参数检查留给helper函数,而是在包装器函数中进行。这样,如果需要,您还可以提供包装器函数的“快速”未检查版本。)
这种方法同样用于将重复的代码重构为帮助程序和实际的公共函数。找到重复的代码,将其移动到单独的内部助手函数,注意参数可能与公共函数使用的参数非常不同-通常,您可能有一个动态分配的缓冲区来将数据放入其中。
没有真正困难的部分,您只需要稍微练习一下,并学会在选择哪些参数传递给helper函数时考虑多个用户(最初是重复的函数)。
在非常复杂的情况下,您可能需要将helper函数拆分为多个helper;拆分为某种类型的helper工具箱。例如,动态内存管理助手函数通常就是这种情况。
例如,如果希望将其转换为生成动态分配字符串的接口,可以使用字符串缓冲区接口,如
struct strbuffer {
    char   *data;
    size_t  size;   /* Number of chars allocated for */
    size_t  used;   /* Number of chars used in data */
};
#define STRBUFFER_INIT { NULL, 0, 0 }

static void strbuffer_addchar(struct strbuffer *ref, const char c);
static char *strbuffer_finalize(struct strbuffer *ref);
static char *strbuffer_finalize_reverse(struct strbuffer *ref);

所以带符号的integer-to-string函数看起来像
char *new_int_to_string(const int value, const size_t radix, const char *digits)
{
    struct strbuffer buf = STRBUFFER_INIT;
    if (value < 0) {
        reverse_radix(&buf, radix, digits, -value);
        strbuffer_addchar(&buf, '-');
    } else
        reverse_radix(&buf, radix, digits, value);
    return strbuffer_finalize_reverse(&buf);
}

reverse_radix()按相反的顺序生成数值,
void reverse_radix(struct strbuffer *ref,
                   const unsigned long radix,
                   const char *digits,
                   unsigned long value)
{
    do {
        strbuffer_addchar(ref, digits[value % radix]);
        value /= radix;
    } while (value > 0);
}

helper函数strbuffer_finalize()将把data字段重新分配到所需的精确长度,包括结束\0的字符串,清除结构,并返回datastrbuffer_finalize_reverse()执行相同的操作,只是先反转内容。这样我们就把递归调用也变成了一个简单的循环。
您甚至可以使用strbuffer辅助函数实现浮点版本;您只需将整数部分与小数部分分开转换(因此还有两个辅助函数)。

关于c - 防止有符号和无符号整数逻辑之间的代码重复,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/49423174/

10-11 22:06
查看更多