我正在通过K& R独自学习C语言。
2-3编写函数htoi(s),它转换十六进制字符串 数字(包括可选的0x或0X)到它的等效整数值。 允许的数字是0到9,a到f和A到F.
我选择将每个有效数字转换为0-15等效数字并忽略无效字符。
我试图不使用前46页中没有提到的任何内容。
我正在使用静态输入,直到我得到htoi()正确。
#include <stdio.h>
#include <ctype.h>
#include <string.h>
int htoi(char s[]);
// hex alpha char to integer
// 0..1 and Aa - fF
int hatoi(char c);
int main()
{
char s[] = "0xfff";
int res; /* result */
res = htoi(s);
printf("%s = %d\n",s,res);
return 0;
}
int hatoi(char c)
{
int res;
if (isdigit(c))
res = c - '0';
else if (c >= 'a' && c <= 'f')
/* offset a..f to 10-14 */
res = (c - '0')-39;
else if (c >= 'A' && c <= 'F')
/* offset a..f to 10-14 */
res = (c - '0')-7;
else
res = 0;
return res;
}
int htoi(char s[])
{
int len,i,result,power,digit;
result = power = 0;
len = strlen(s)-1;
for (i=len; i >= 0; --i) {
digit = hatoi(s[i]);
if (digit) {
if (power == 0) {
result += digit;
power++;
} else
result += digit * power;
power *= 16;
}
}
return result;
}
我关门了吗?它似乎工作正常。我想确保我没有学习坏习惯,并且我正在抓住第2章我应该做的事情。
答案 0 :(得分:2)
一些想法:
如果您的字符串中间包含0
,您的代码将无效;考虑您对hatoi
的返回值进行的测试。
数字39
和7
似乎有些神奇。如果您可以在代码中明确地派生它们,那就更清楚了。
最好将if
或else
之后的代码块放在大括号中,即使它只是一个语句。
为什么不将power
初始化为1
?这样,您不需要循环中的特殊情况逻辑。
答案 1 :(得分:1)
我将int
用于hatoi()
参数:int hatoi(int ch);
您无法区分'0'
中的有效hatoi()
和无效字符。
htoi()
函数可以简化很多。例如,if (digit)
测试是不必要的(您从strlen-1循环到开头)。
答案 2 :(得分:1)
isdigit
是否仅限于0-9,还是受区域设置影响?不想要后者。
res = (c - '0')-39;
应为res = (c - 'a')+10;
res = (c - '0')-7;
应为res = (c - 'A')+10;
请注意,这仅适用于基于ASCII的计算机。 EBCDIC机器上的数字和/或字母不是连续的。
参数应该是const
指针。
htoi
非常复杂。您应该发现num = (num << 4) | digit;
非常有用。
int htoi(const char *s)
{
int result = 0;
while (*s)
result = ( result << 4 ) | hatoi(*(s++));
return result;
}
您可能想要检查溢出。
答案 3 :(得分:1)
int hatoi(char c); /*** I'd suggest a longer, descriptive name
such as parse_hexdigit ***/
int main()
{
char s[] = "0xfff"; /*** Is this a good test case?
It is a palindrome with no numbers 0-9 ***/
…
}
int hatoi(char c)
{
int res;
if (isdigit(c))
res = c - '0';
else if (c >= 'a' && c <= 'f')
/* offset a..f to 10-14 */ /*** 10 - 15 ***/
res = (c - '0')-39; /*** res = c - 'a' + 10 is clearer ***/
else if (c >= 'A' && c <= 'F')
/* offset a..f to 10-14 */
res = (c - '0')-7; /*** res = c - 'A' + 10 is clearer ***/
else
res = 0;
return res;
}
int htoi(char s[])
{
int len,i,result,power,digit;
result = power = 0;
len = strlen(s)-1; /*** Check for overflow when you can ***/
for (i=len; i >= 0; --i) { /*** Avoid iterating backwards ***/
digit = hatoi(s[i]);
if (digit) {
if (power == 0) {
result += digit;
power++;
} else /*** Use a consistent pattern of braces ***/
result += digit * power;
power *= 16;
}
}
return result;
}
我会这样写:
unsigned htoi( char *s ) {
unsigned acc = 0;
if ( * s != '0' ) return 0;
++ s;
if ( * s != 'x' || * s != 'X' ) return 0;
++ s;
/* Check that multiplication by 16 will not overflow */
while ( acc < UINT_MAX / 16 ) {
if ( * s >= '0' && * s <= '9' ) {
acc *= 16;
acc += * s - '0';
} else if ( * s >= 'A' && * s <= 'F' ) {
acc *= 16;
acc += * s - 'A' + 10;
} else if ( * s >= 'a' && * s <= 'f' ) {
acc *= 16;
acc += * s - 'a' + 10;
} else {
return acc; /* handles end of string or just end of number */
}
++ s;
}
return acc;
}
答案 4 :(得分:1)
总的来说,这看起来很不错。我会在内容中添加一些内容,我认为你可以改进这些内容。
int hatoi(char c)
{
int res;
if (isdigit(c))
res = c - '0';
没有真正的理由来创建res
变量。您总是返回在该变量中设置的任何内容,并且永远不会更改它。为什么不将res = c - '0'
和后来的return res
替换为return c - 0
?
else if (c >= 'a' && c <= 'f')
/* offset a..f to 10-14 */
res = (c - '0')-39;
这似乎有点令人费解。为什么要减去'0'
然后39
?说(c - 'a') + 10
会更清楚。此外,评论错误,应该说10-15
。
result = power = 0;
len = strlen(s)-1;
for (i=len; i >= 0; --i) {
你的循环遍及整个字符串;但是在像0xabcd
这样的十六进制字符串中,0x
可能不应被视为您正在解析的数字的一部分。你这样做的方法,将未知字符视为0,对你的测试字符串来说无关紧要,但如果你开始时有其他东西(比如1230xabcd
),你会得到一个相当奇怪的结果。我建议检查前两个字符实际上是0x
(如果没有,可能会返回0
),然后循环到2
而不是0
。
digit = hatoi(s[i]);
if (digit) {
如果数字不为零,您似乎只会增加power
。因此,对于像0x0102
这样的数字,您将获得18,而不是正确的结果258.不需要进行if (digit)
检查。如果您想要在无效字符的情况下从hatoi
返回定点,以便忽略它们,我建议您返回-1
,然后选中if (digit >= 0)
。
if (power == 0) {
result += digit;
power++;
如果您将power
初始化为1
而不是0
,则不必具备此特殊情况。
} else
result += digit * power;
power *= 16;
}
}
答案 5 :(得分:0)
处理正十六进制数字的简单版本
int htoi(const char s[]) {
unsigned int n = 0;
int i = 0;
if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')){
i += 2;
}
int isValid = 1; // check if hexdigit is valid
for (; isValid; i++) {
if (s[i] >= '0' && s[i] <= '9') {
n = 16 * n + (s[i] - '0');
}
else if (s[i] >= 'a' && s[i] <= 'f') {
n = 16 * n + (s[i] - 'a' + 10);
}
else if (s[i] >= 'A' && s[i] <= 'F') {
n = 16 * n + (s[i] - 'A' + 10);
}
else {
isValid = 0;
}
}
return n;
}