我第一次尝试K& R 2-3的十六进制到整数htoi()

时间:2011-04-15 22:16:55

标签: c

我正在通过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章我应该做的事情。

6 个答案:

答案 0 :(得分:2)

一些想法:

  • 如果您的字符串中间包含0,您的代码将无效;考虑您对hatoi的返回值进行的测试。

  • 数字397似乎有些神奇。如果您可以在代码中明确地派生它们,那就更清楚了。

  • 最好将ifelse之后的代码块放在大括号中,即使它只是一个语句。

  • 为什么不将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;
}