我有以下函数:第一个函数打印给定基数(基数)中的无符号整数。
第二个函数的作用完全相同,但使用有符号整数。
如您所见,这些函数的主体完全相同。为了防止同一代码加倍,我已经挣扎了几个小时,但我找不到解决这个问题的方法。
无符号函数:
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
的字符串,清除结构,并返回data
;strbuffer_finalize_reverse()
执行相同的操作,只是先反转内容。这样我们就把递归调用也变成了一个简单的循环。您甚至可以使用strbuffer辅助函数实现浮点版本;您只需将整数部分与小数部分分开转换(因此还有两个辅助函数)。
关于c - 防止有符号和无符号整数逻辑之间的代码重复,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/49423174/