C - getchar() неправильно считывает второй символ ввода

Я пытаюсь использовать getchar() для чтения всех цифр входного числа и сохранения их в массиве. Но каждый раз, когда я запускаю программу, что-то идет не так со второй цифрой.

Вот мой код:

int ch = 0;
int digits[0];
int i = 0;

while ((ch = getchar()) != '\n') {
    digits[i] = ch - '0';
    i++;
}

Чтобы показать, что пойдет не так, я вставил два printf:

while ((ch = getchar()) != '\n') {
    printf("%d ", ch);
    digits[i] = ch - '0';
    printf("%d\n", ch);
    i++;
}

Например, когда я ввожу 1100, я получаю:

49 49

49 1

48 48

48 48

И когда я печатаю массив в отдельном цикле, вывод:

1 10 0 0

Когда я ввожу 66666, я получаю:

54 54

54 6

54 54

54 54

54 5

И массив:

6 10 6 6 6

Я пробовал кучу других чисел с разной длиной, каждый раз такая же странная вещь происходит со второй цифрой, и только со второй. Кто-нибудь знает, почему это так? Большое спасибо!


person yixiaoyx    schedule 21.08.2016    source источник
comment
int digits[0]; вы уверены, что вам нужен массив нулевого размера?   -  person tkausl    schedule 21.08.2016
comment
О чем следует подумать: что происходит в конце файла?   -  person too honest for this site    schedule 21.08.2016


Ответы (4)


каждый раз, когда я запускаю программу, что-то идет не так со второй цифрой.

Это связано с тем, что вы объявили свой массив digit как zero-size array, а компилятор не выделяет памяти для вашего массива. Таким образом, запись в любую позицию в массиве фактически приведет к записи в области памяти, выделенные для других целей (например, выделенные для других переменных), а также она будет иметь undefined behavior.

Итак, результат, который вы получили, связан с этим UNDEFINED BEHAVIOR, и в вашем случае запись в любую позицию с использованием индексов с digit фактически записывает в память, выделенную для других переменных в вашей программе, и ячейка памяти, которую вы получаете с помощью digit[1], оказывается ячейкой памяти выделенный переменной ch типа char в вашей программе. Следовательно, запись в ячейку памяти digit[1] изменит вашу переменную ch. Поскольку это undefined behavior, вы можете получить Segmentation Fault для своей программы.

Чтобы решить вашу проблему, объявите массив digit достаточного размера.

#define SIZE_DIGITS  /* Sufficient Size required by your application */
int digit[SIZE_DIGITS];

Однако в стандартном C и C++ zero-size array не разрешено, и если вы используете GCC, скомпилируйте его с параметром -pedantic. Он даст предупреждение, говоря:

zero.c:3:6: warning: ISO C forbids zero-size array 'a' [-pedantic]

В случае C++ выдает аналогичное предупреждение.

Кроме того, массивы нулевого размера очень полезны в качестве последнего элемента структуры, которая на самом деле является заголовком для объекта переменной длины. Они разрешены в GNU C. Структура, имеющая zero-size array в качестве последнего элемента, называется variable-length structure. Подробнее об этом можно прочитать здесь.

person abhiarora    schedule 21.08.2016
comment
Большое спасибо, ваш ответ очень подробный. - person yixiaoyx; 23.08.2016

int digits[0];

У вас есть массив, содержащий нулевые цифры.

int digits[32];     /* or some reasonable size, check for overflow... */
person John Hascall    schedule 21.08.2016
comment
Это не отвечает на вопрос. Он просто сообщает им, как решить проблему, а не почему это происходит, что на самом деле спросили. - person Hermann Döppes; 21.08.2016

Как уже было сказано два раза, ваш массив имеет нулевой размер и поэтому не может вместить все числа, которые вы пытались туда засунуть.

Таким образом, при записи в любую позицию в массиве вы на самом деле пишете в память, которая используется для других целей, например для хранения ch. Видимо так уж получилось, что место, где ваша программа ожидает (несуществующее) digits[1], хранится ch. Следовательно, вы перезаписываете ch на ch - '0'.

Затем, выходя из области ch, программа может писать туда другие вещи. (Потому что очевидно, что память больше не нужна.) Очевидно, он решает записать что-то, имеющее значение 10. Это приводит к «странному» выводу.

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

person Hermann Döppes    schedule 21.08.2016
comment
Вы действительно ответили на мой вопрос, спасибо. Если бы я мог принять два ответа, это был бы ваш и тот, который я выбрал сейчас :) - person yixiaoyx; 23.08.2016

Есть ряд небольших проблем с размещенным кодом:

  1. массив digits[] имеет размер 0, поэтому не может содержать никаких цифр, поэтому попытка поместить что-либо в digits[x] повреждает стек

  2. цикл while(), даже если digits[] был правильно объявлен, не перестанет вводить символы (если не EOF и не новая строка), поэтому может выйти за пределы любого размера массива

  3. размещенный код не следит за условием EOF

вот некоторый код, который будет работать правильно и допускает не более 32 цифр

#include <stdio.h>

#define MAX_DIGITS (32)

int main( void )
{

    int ch = 0;
    int digits[ MAX_DIGITS ];


    int i = 0;
    while ( i < MAX_DIGITS && ( (ch = getchar()) != EOF && '\n' !=  ch) ) 
    {
        printf("%d ", ch);
        digits[i] = ch - '0';
        printf("%d\n", digits[i]);
        i++;
    }
}

вот пример ввода

1 2 3 4 5 6

и полученный результат

49 1
32 -16
50 2
32 -16
51 3
32 -16
52 4
32 -16
53 5
32 -16
54 6

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

person user3629249    schedule 22.08.2016
comment
@chux, всегда есть вероятность, что я смотрел МОЮ первую запись оператора while() с дополнительной проверкой условия EOF. Я отредактирую свой ответ, чтобы удалить текущий № 3 - person user3629249; 22.08.2016
comment
Спасибо, ваш ответ тоже очень полезен. - person yixiaoyx; 23.08.2016