我有以下功能:第一个在给定的基数(基数)中打印无符号整数。
第二个函数完全相同,但使用有符号整数。如您所见,这些函数的主体完全相同。为了防止将相同的代码加倍,我已经努力了几个小时,但我找不到解决此问题的方法。
无符号函数:
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
,因为它应该只在这个编译单元(文件)中可见,而不能直接在外部调用。
辅助函数的目的是打印一个数字。如果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);
}
我特意添加了输出流标识符并更改了参数顺序,以便更容易理解可见函数(有时也称为包装器函数,如果它们非常简单)与执行实际操作的实际内部辅助函数有何不同工作。
我在包装函数中添加了基数和数字检查,因为这是推荐的做法。(也就是说,不要将参数检查留给辅助函数,而是在包装函数中进行。这样,如果需要,您还可以提供“快速”/未检查版本的包装函数。)
同样的方法用于将重复的代码重构为帮助程序和实际的公共函数。您找到重复的代码,将其移动到一个单独的内部辅助函数,注意参数可能与公共函数使用的非常不同——通常,您可能有一个动态分配的缓冲区来放置数据。
没有真正困难的部分,你只需要练习一点,并学会在选择将哪些参数传递给辅助函数时考虑多个用户——最初重复的函数。
在非常复杂的情况下,您可能需要将辅助函数拆分为多个辅助函数;进入某种辅助工具箱。这通常是动态内存管理辅助函数的情况。
例如,如果你想把它变成一个产生动态分配字符串的接口,你可以使用一个字符串缓冲区接口,比如
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 助手实现浮点版本;您只需将整数部分与小数部分分开转换(还有两个辅助函数)。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句