Преобразование строковой константы в числовое значение с использованием C - PullRequest
1 голос
/ 07 марта 2019

Я написал программу на C, которая использует два разных алгоритма для преобразования строковой константы, представляющей числовое значение, в ее целочисленное значение.По некоторым причинам первый алгоритм, atoi (), не выполняется должным образом при больших значениях, в то время как второй алгоритм, atoi_imp (), работает нормально.Это проблема оптимизации или какая-то другая ошибка?Проблема в том, что первая функция завершает процесс программы с ошибкой.

#include <stdio.h>
#include <string.h>

unsigned long long int atoi(const char[]);
unsigned long long int atoi_imp(const char[]);

int main(void) {
    printf("%llu\n", atoi("9417820179"));
    printf("%llu\n", atoi_imp("9417820179"));
    return 0;
}

unsigned long long int atoi(const char str[]) {
    unsigned long long int i, j, power, num = 0;
    for (i = strlen(str) - 1; i >= 0; --i) {
        power = 1;
        for (j = 0; j < strlen(str) - i - 1; ++j) {
            power *= 10;
        }
        num += (str[i] - '0') * power;
    }
    return num;
}

unsigned long long int atoi_imp(const char str[]) {
    unsigned long long int i, num = 0;
    for (i = 0; str[i] >= '0' && str[i] <= '9'; ++i) {
        num = num * 10 + (str[i] - '0');
    }
    return num;
}

Ответы [ 4 ]

3 голосов
/ 07 марта 2019

atoi является частью стандартной библиотеки C, с подписью int atoi(const char *);.

Вы заявляете, что функция с таким именем существует, но присваивает ей другой тип возвращаемого значения.Обратите внимание, что в C имя функции является единственным, что имеет значение, и набор инструментов может доверять только тому, что вы говорите в исходном коде.Если вы врете компилятору, как здесь, все ставки выключены.

Вы должны выбрать другое имя для своей собственной реализации, чтобы избежать проблем.


Как исследовано @pmg, CСтандарт (ссылка на C99.7.1.3 ) говорит, что использование имен из стандартной библиотеки C для ваших собственных глобальных символов (функций или глобальных переменных) явно Неопределенное поведение .Остерегайтесь носовых демонов!

2 голосов
/ 07 марта 2019

Хорошо, есть хотя бы одна проблема с вашей функцией atoi.
Вы зацикливаетесь на значении без знака и проверяете, больше ли оно равно нулю, что должно быть понижением.

Самое простое решениесмещение индекса, т.е.:

unsigned long long int my_atoi(const char str[]) {
    unsigned long long int i, j, power, num = 0;
    for (i = strlen(str); i != 0; --i) {
        power = 1;
        for (j = 0; j < strlen(str) - i; ++j) {
            power *= 10;
        }
        num += (str[i-1] - '0') * power;
    }
    return num;
}
1 голос
/ 07 марта 2019

Ваша функция имеет бесконечный цикл: так как i без знака, i >= 0 всегда верно.

Это может быть улучшено различными способами:

  • Вы должны вычислить длину str только один раз. strlen() недешево, он должен сканировать строку, пока не найдет нулевой терминатор. Компилятор не всегда способен оптимизировать избыточные вызовы для одного и того же аргумента.

  • power можно вычислять постепенно, избегая необходимости во вложенном цикле.

  • вам не следует использовать имя atoi, так как это стандартная функция в библиотеке C. Если вы не реализуете его спецификацию точно и правильно, вы должны использовать другое имя.

Вот исправленная и улучшенная версия:

unsigned long long int atoi_power(const char str[]) {
    size_t i, len = strlen(str);
    unsigned long long int power = 1, num = 0;
    for (i = len; i-- > 0; ) {
        num += (str[i] - '0') * power;
        power *= 10;
    }
    return num;
}

Измененная таким образом, функция должна иметь производительность, аналогичную версии atoi_imp. Обратите внимание, что они не реализуют одну и ту же семантику. atoi_pow должна быть задана строка цифр, тогда как atoi_imp может содержать завершающие символы.

На самом деле ни atoi_imp, ни atoi_pow не реализуют спецификацию atoi, расширенную для обработки больших целых чисел без знака:

  • atoi игнорирует любые начальные пробельные символы,
  • atoi принимает необязательный знак, либо '+', либо '-'.
  • atoi использует все последующие десятичные цифры, поведение при переполнении не определено.
  • atoi игнорирует и завершает символы, которые не являются десятичными цифрами.

Учитывая эту семантику, естественная реализация или atoi - это реализация atoi_imp с дополнительными тестами. Обратите внимание, что даже strtoull(), который вы можете использовать для реализации своей функции, обрабатывает пробелы и необязательный знак, хотя преобразование отрицательных значений может дать удивительные результаты.

1 голос
/ 07 марта 2019

Слишком поздно, но может помочь.Я сделал для базы 10, в случае, если вы измените базу, вам нужно позаботиться о том, как вычислить цифру 0, в *p-'0'.

Я бы использовал правило Хорнера для вычислениязначение.

#include <stdio.h>
void main(void)
{
  char *a = "5363", *p = a;
  int unsigned base = 10;
  long unsigned x = 0;
  while(*p) {
    x*=base;
    x+=(*p-'0');
    p++;
  }
  printf("%lu\n", x);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...