Почему я получаю этот неожиданный результат, используя atoi () в C? - PullRequest
10 голосов
/ 28 апреля 2010

Я не понимаю результатов следующего кода C.

main()
{
    char s[] = "AAA";
    advanceString(s);
}

void advanceString(p[3])
{
    int val = atoi(p);
    printf("The atoi val is %d\n",val);
}

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

Ответы [ 4 ]

53 голосов
/ 28 апреля 2010

atoi() преобразует строковое представление целого числа в его значение. Он не будет преобразовывать произвольные символы в их десятичное значение. Например:

int main(void)
{
    const char *string="12345";

    printf("The value of %s is %d\n", string, atoi(string));

    return 0;
}

В стандартной библиотеке C нет ничего, что могло бы преобразовать «A» в 65 или «Z» в 90, вам нужно написать это самостоятельно, особенно для любой кодировки, которую вы ожидаете ввести.

Теперь, когда вы знаете, что делает atoi(), , пожалуйста, не используйте его , чтобы иметь дело с числовым вводом во всем, что вы придумали. Вы действительно должны иметь дело с вводом, а не с тем, что вы ожидаете. Хм, а что будет, когда я введу 65 вместо A? Учителя любят ломать вещи.

atoi() не выполняет никакой проверки ошибок, что делает все, что полагается на него, для преобразования произвольного ввода хрупким, в лучшем случае. Вместо этого используйте strtol() (пример, ориентированный на POSIX):

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

int main(void)
{
    static const char *input ="123abc";
    char *garbage = NULL;
    long value = 0;

    errno = 0;

    value = strtol(input, &garbage, 0);

    switch (errno) {
        case ERANGE:
            printf("The data could not be represented.\n");
            return 1;
        // host-specific (GNU/Linux in my case)
        case EINVAL:
            printf("Unsupported base / radix.\n");
            return 1;
    }

    printf("The value is %ld, leftover garbage in the string is %s\n",
           // Again, host-specific, avoid trying to print NULL.
           value, garbage == NULL ? "N/A" : garbage);

    return 0;
}

При запуске это дает:

Значение 123, остаток мусора в строка abc

Если вы не заботитесь о сохранении / проверке мусора, вы можете установить второй аргумент на NULL. Там нет необходимости free(garbage). Также обратите внимание, что если вы передадите 0 в качестве третьего аргумента, предполагается, что входные данные - это желаемое значение десятичного, шестнадцатеричного или восьмеричного представления. Если вам нужно основание 10, используйте 10 - он потерпит неудачу, если вход не соответствует ожидаемому.

Вы также проверите возвращаемое значение для максимального и минимального значения, которое может обработать long int. Однако, если оба возвращаются для указания ошибки, устанавливается errno. Упражнение для читателя - изменить *input с 123abc на abc123.

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

Для вашей реализации самый простой совет, который я могу вам дать, - это серия переключателей, например:

// signed, since a return value of 0 is acceptable (NULL), -1
// means failure
int ascii_to_ascii_val(const char *in)
{
    switch(in) {
        // 64 other cases before 'A'
        case 'A':
           return 65;
        // keep going from here
        default:
            return -1; // failure

}

.. затем просто запустите это в цикле.

Или, предварительно заполни словарь, чтобы функция поиска могла охватывать (лучше). Вам не понадобятся хэши, только хранилище ключей -> значений, поскольку вы заранее знаете, что оно будет содержать, где стандартные символы ASCII - это ключи, а соответствующие им идентификаторы - значения.

12 голосов
/ 28 апреля 2010

Он пытается преобразовать строку в целое число. Поскольку AAA не может быть преобразовано в целое число, значение равно 0. Попробуйте дать ему 42 или что-то еще.

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

См. http://www.cplusplus.com/reference/clibrary/cstdlib/atoi/

6 голосов
/ 28 апреля 2010

Считайте atoi() как от a до i (от ASCII до целого числа).

atoi() преобразует строку, представляющую десятичное число, в целое число.

char s[] = "42";
int num = atoi(s); //The value of num is 42.
2 голосов
/ 28 апреля 2010

atoi ожидает, что ее аргумент будет строковым представлением десятичной (base-10) целочисленной константы; AAA не является допустимой десятичной целочисленной константой, поэтому atoi возвращает 0, потому что у него нет другого способа указать, что ввод неверен.

Обратите внимание, что atoi преобразует до первого символа, который не является частью допустимой целочисленной константы; другими словами, «123» и «123w» будут преобразованы в 123.

Как все говорят, не используйте atoi; используйте вместо этого strtol.

...