防止有符号和无符号整数逻辑之间的代码重复

时间:2018-03-22 07:47:59

标签: c

我有以下功能:第一个打印给定基数(基数)中的无符号整数 第二个函数完全相同但带有有符号整数。 如您所见,这些功能的主体完全相同。我一直在努力工作几个小时,以防止相同的代码加倍,我找不到解决这个问题的方法。

无符号功能

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

2 个答案:

答案 0 :(得分:4)

也许你想要这个:

#include <math.h>
...
int print_int_helper(int n, int radix, const char *digit) {
  if (n < 0)
  {
    putchar('-');
  }

  return print_int_helper_unsigned(abs(n), radix, digit);
}

答案 1 :(得分:0)

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

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,因为它只能在此编译单元(文件)中可见,而不能在外部直接调用。

辅助功能的目的是打印出一个数字。如果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);
}

我故意添加了输出流标识符并更改了参数顺序,以便更容易理解可见函数(有时也称为包装函数,如果它们非常简单)可以与实际工作的实际内部帮助函数不同。

我将基数和数字检查添加到包装函数中,因为这是推荐的做法。 (也就是说,不要将参数检查留给辅助函数,而是在包装函数中执行。如果需要,也可以提供&#34;快速&#34; /未检查的包装函数版本。)< / p>

这种方法用于将重复的代码重构为帮助程序和实际的公共函数。你找到重复的代码,将它移动到一个单独的内部帮助函数,注意参数可能与公共函数使用的非常不同 - 通常,你可能有例如一个动态分配的缓冲区,用于放入数据。

没有真正困难的部分,你只需要练习一下,并在选择将哪些参数传递给辅助函数时学会考虑多个用户 - 最初重复的函数。

在非常复杂的情况下,您可能需要将辅助函数拆分为多个帮助程序;进入某种帮助工具箱。这通常是例如动态内存管理辅助函数。

例如,如果要将其转换为生成动态分配字符串的接口,则可以使用字符串缓冲区接口,例如

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

这样有符号的整数到字符串函数可能看起来像

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

辅助函数strbuffer_finalize()会将data字段重新分配到所需的确切长度,包括字符串终止\0,清除结构,然后返回data; strbuffer_finalize_reverse()执行相同操作,但首先撤消内容。这样我们也可以将递归调用转换为一个简单的循环。

您甚至可以使用strbuffer助手实现浮点版本;你只是将小数部分与小数部分分开转换(所以还有两个辅助函数)。