Моя первая попытка гекса K & R 2-3 к целому числу htoi () - PullRequest
0 голосов
/ 16 апреля 2011

Я работаю через 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.

Ответы [ 5 ]

2 голосов
/ 16 апреля 2011

Некоторые мысли:

  • Ваш код не будет работать, если ваша строка содержит 0 в середине; рассмотрим тест, который вы делаете для возвращаемого значения hatoi.

  • Числа 39 и 7 кажутся немного волшебными. Будет понятнее, если вы сможете получить их явно в коде.

  • Рекомендуется всегда ставить блоки кода после if или else в фигурных скобках, даже если это всего лишь один оператор.

  • Почему бы не инициализировать power как 1? Таким образом, вам не нужна эта особая логика в вашем цикле.

1 голос
/ 16 апреля 2011

В целом, это выглядит довольно хорошо.Я добавлю несколько комментариев для вещей, которые, я думаю, вы могли бы улучшить.

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

Youкажется, увеличивается только 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;
    }
  }
1 голос
/ 16 апреля 2011
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;
}
1 голос
/ 16 апреля 2011

Ограничено ли 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;
}

Вы можете проверить наличие переполнения.

1 голос
/ 16 апреля 2011
  • Я бы использовал int для hatoi() параметра: int hatoi(int ch);

  • Вы не можете отличить действительный '0' от недопустимого символа в hatoi().

  • Функцию htoi() можно немного упростить. Например, тест if (digit) не нужен (вы переходите от strlen-1 к началу).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...