пример "строки цифр в ее числовом эквиваленте" не работает должным образом согласно K & R - PullRequest
0 голосов
/ 16 января 2019

Я пытаюсь выяснить функцию atoi() из stdlib.h. Согласно K & R это выглядит следующим образом:

int atoi(char s[]) {
   int n, i;

   n = 0;
   for (i = 0; s[i] >= '0' && s[i] <= '9'; ++i)
        n = 10 * n + (s[i] - '0');
   return n;
}

Как я понимаю, функция atoi() из stdlib.h должна получать строку любых символов в качестве входных данных и буквально выводить только цифры, как показано ниже:

Код 1:

#include <stdio.h>
#include <stdlib.h>

int main(void) {
    printf("%i", atoi(" -123junk"));
    printf("%i", atoi("0"));
    printf("%i", atoi("junk"));         // no conversion can be performed
    printf("%i", atoi("2147483648"));   // UB: out of range of int
}

Выход:

-123
0
0
-2147483648

Однако в моей программе я пытаюсь предоставить строку в качестве входных данных и получать цифры только в качестве выходных данных:

Код 2:

#include <stdio.h>
#include <stdlib.h>

int main() {
    int c, i;
    char s[i];
    for (i = 0; (c = getchar()) != '\n'; ++i)
       s[i] = c;
    s[i] = '\n';
    s[++i] = '\0';
    printf("%i", atoi(s));
}

При выполнении на машине:

pi@host:~/new$ cc new.c
pi@host:~/new$ a.out
21412421How it is
0

Я получаю неверные значения в качестве вывода.

Вопросы:

1) В соответствии с кодом 1, printf("%i", atoi(" -123junk")), похоже, что функция atoi() может принимать строку в качестве аргумента и возвращать одно целое число, представляющее конкатенацию числовых значений цифр во входной строке, не это?

2) Что возвращает atoi() из stdlib.h?

3) Как исправить функцию main() из кода 2, чтобы получить символы из stdin, записать в массив, вызвать функцию atoi(), предоставить массив в качестве аргумента, получить буквально цифры на выходе?

4) Согласно примеру K & R функции atoi(), «выражение (s[i] - '0') - это числовое значение символа, хранящегося в s[i]», но зачем нам нужно добавлять 10 * n часть, более того? , чтобы присвоить n 0 перед ним, как n * 0 = 0, следовательно, n * 10 = 0, что означает, что n * 10 всегда будет нулем в операторе присваивания n = 10 * n + (s[i] - '0');, следовательно, зачем нам это?

5) Если atoi () возвращает одно целое число, как результат printf("%i", atoi(" -123junk")); может вернуть строку с числами -123? Другими словами, правильно ли я понимаю: функция atoi() вызывается внутри функции printf() с аргументом " -123junk". Функция atoi() возвращает целое число, ТОЛЬКО одно целое число, что-то вроде n = 10 * n + (s[i] - '0'); чем, как его можно развернуть в -123 ??

1 Ответ

0 голосов
/ 16 января 2019

Q1) ваша версия atoi() слишком проста, стандартная версия игнорирует начальные пробельные символы и обрабатывает необязательный знак перед числом. atoi(" -123junk") следует оценить до -123.

Q2) atoi - это стандартная функция, определенная прототипом int atoi(const char *s);, она возвращает целое число.

Q3) В Code 2 есть несколько ошибок:

  • Вы указываете размер массива char s как i, который не инициализируется. вместо этого вы должны определить массив с достаточно большим значением, таким как 64,
  • вы должны проверить на потенциальное переполнение буфера в цикле,
  • Вы должны проверить EOF, чтобы остановить цикл в случае, если конец файла встречается без новой строки.

Вот модифицированная версия:

#include <stdio.h>
#include <stdlib.h>

int main() {
    char s[64];
    size_t i;
    int c;
    for (i = 0; i < sizeof(s) - 1 && (c = getchar()) != EOF;) {
       s[i++] = c;
       if (c == '\n')
           break;
    }
    s[i] = '\0';
    printf("%i", atoi(s));
    return 0;
}

Q4) выражение n = 10 * n + (s[i] - '0') вычисляется для каждой новой цифры, найденной в строке. Действительно, немного неэффективно умножать текущее значение на 10, если не было обнаружено ненулевой цифры, но написание функции таким способом просто.

Чтобы избежать этих бесполезных умножений, вот альтернатива:

int atoi(const char *s) {
    int n = 0;
    size_t i = 0;

    while (s[i] == '0')
        i++;
    if (s[i] >= '1' && s[i] <= '9') {
        n = s[i++] - '0';
        while (s[i] >= '0' && s[i] <= '9')
            n = 10 * n + (s[i++] - '0');
    }
    return n;
}

Но эта функция более громоздка и может фактически быть менее эффективной, чем простая версия. Попробуйте и сравните тесты в вашей системе.

Для полноты, вот полная переносимая версия, использующая ctype.h>, которая обрабатывает необязательный начальный пробел и необязательный знак. Он также обрабатывает переполнение с определенным поведением, хотя для этого не требуется стандартная версия atoi().

#include <limits.h>
#include <stdio.h>

int atoi(const char *s) {
    int n = 0, d;

    /* skip optional initial white space */
    while (isspace((unsigned char)*s))
        s++;
    if (*s == '-') {
        /* convert negative number */
        s++;
        while (isdigit((unsigned char)*s)) {
            d = (*s++ - '0');
            /* check for potential arithmetic overflow */
            if (n < INT_MIN / 10 || (n == INT_MIN / 10 && -d < INT_MIN % 10)) {
                n = INT_MIN;
                break;
            }
            n = n * 10 - d;
        }
    } else {
        /* ignore optional positive sign */
        if (*s == '+')
            s++;
        while (isdigit((unsigned char)*s)) {
            d = (*s++ - '0');
            /* check for potential arithmetic overflow */
            if (n > INT_MAX / 10 || (n == INT_MAX / 10 && d > INT_MAX % 10)) {
                n = INT_MAX;
                break;
            }
            n = n * 10 + d;
        }
    }
    return n;
}

int main(int argc, char *argv[]) {
    int i, n;

    for (i = 1; i < argc; i++) {
        n = atoi(argv[i]);
        printf("\"%s\" -> %d\n", argv[i], n);
    }
    return 0;
}
...