Как заполнить массив с клавиатуры на C: от статических до динамических массивов

Ввод массива с клавиатуры в языке C требует понимания работы с указателями, циклами и функциями ввода-вывода. Наиболее частая ошибка начинающих — использование неинициализированного указателя или выход за границы массива при ручном вводе размерности. Если ваша программа завершается с ошибкой Segmentation fault или выводит мусор вместо чисел, проблема именно в неправильном заполнении массива через scanf().

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

1. Статический массив фиксированного размера

Самый простой способ — заранее объявить массив с известным количеством элементов. Например, для массива из 10 целых чисел код будет выглядеть так:

#include <stdio.h>

int main() {

int arr[10]; // Статический массив на 10 элементов

printf("Введите 10 целых чисел:\n");

for (int i = 0; i < 10; i++) {

scanf("%d", &arr[i]);

}

return 0;

}

⚠️ Внимание: Если пользователь введёт меньше 10 чисел, оставшиеся элементы массива останутся неинициализированными. Это может привести к неопределённому поведению программы при дальнейшей работе с массивом.

  • ✅ Простота реализации
  • ✅ Нет риска утечек памяти
  • ❌ Фиксированный размер (нельзя ввести больше/меньше элементов)
  • ❌ Оставшиеся элементы не очищаются автоматически

Для очистки неиспользуемых ячеек добавьте инициализацию нулями:

int arr[10] = {0}; // Все элементы изначально равны 0

2. Динамический массив с предварительным вводом размера

Если размер массива заранее неизвестен, используйте динамическое выделение памяти с помощью malloc() или calloc(). Пример:

#include <stdio.h>

#include <stdlib.h>

int main() {

int n;

printf("Введите количество элементов: ");

scanf("%d", &n);

int arr = (int)malloc(n * sizeof(int)); // Выделение памяти

if (arr == NULL) {

printf("Ошибка выделения памяти!\n");

return 1;

}

printf("Введите %d чисел:\n", n);

for (int i = 0; i <n; i++) {

scanf("%d", &arr[i]);

}

free(arr); // Освобождение памяти

return 0;

}

⚠️ Внимание: Всегда проверяйте результат malloc() на NULL. В системах с ограниченной памятью (например, встраиваемые устройства) выделение может завершиться неудачей.

Функция Особенности Когда использовать
malloc() Выделяет память без инициализации (мусорные значения) Когда скорость важнее безопасности
calloc() Выделяет память и обнуляет все байты Когда нужны нулевые начальные значения
realloc() Изменяет размер ранее выделенного блока Для динамического расширения массива
📊 Какой способ выделения памяти вы используете чаще?
malloc
calloc
realloc
Не использую динамическую память

3. Защита от некорректного ввода

Функция scanf() не проверяет корректность введённых данных. Если пользователь введёт символ вместо числа, программа зависнет или будет работать непредсказуемо. Решение — проверка возвращаемого значения scanf():

for (int i = 0; i <n; i++) {

while (scanf("%d", &arr[i]) != 1) {

printf("Ошибка! Введите целое число: ");

while (getchar() != '\n'); // Очистка буфера ввода

}

}

Альтернативный вариант — использование fgets() + sscanf() для чтения всей строки и последующего парсинга:

char buffer[100];

for (int i = 0; i <n; i++) {

fgets(buffer, sizeof(buffer), stdin);

while (sscanf(buffer, "%d", &arr[i]) != 1) {

printf("Ошибка! Повторите ввод: ");

fgets(buffer, sizeof(buffer), stdin);

}

}

4. Заполнение двумерного массива (матрицы)

Для ввода матрицы с клавиатуры используйте вложенные циклы. Пример для статического двумерного массива 3×3:

int matrix[3][3];

printf("Введите элементы матрицы 3x3:\n");

for (int i = 0; i <3; i++) {

for (int j = 0; j <3; j++) {

scanf("%d", &matrix[i][j]);

}

}

Для динамического выделения памяти под матрицу:

int rows, cols;

printf("Введите количество строк и столбцов: ");

scanf("%d %d", &rows, &cols);

// Выделение памяти под указатели на строки

int matrix = (int)malloc(rows sizeof(int));

for (int i = 0; i <rows; i++) {

matrix[i] = (int*)malloc(cols * sizeof(int));

}

// Заполнение

for (int i = 0; i <rows; i++) {

for (int j = 0; j <cols; j++) {

scanf("%d", &matrix[i][j]);

}

}

Как освободить память для динамической матрицы?

Для освобождения памяти двумерного динамического массива используйте обратный порядок:

for (int i = 0; i <rows; i++) {

free(matrix[i]);

}

free(matrix);

5. Оптимизация для больших массивов

При работе с массивами размером >10 000 элементов:

  • 🔹 Используйте calloc() вместо malloc() для автоматического обнуления памяти
  • 🔹 Читайте данные блоками (например, по 1000 элементов) с паузой
  • 🔹 Для файлового ввода используйте fread() вместо scanf()
  • 🔹 Рассмотрите возможность использования memory-mapped files (отображение файлов в память)

Критическая ошибка: При чтении больших массивов через scanf() буфер ввода может переполниться. Всегда ограничивайте длину вводимой строки или используйте fgets().

1. Проверить доступную память (особенно на встраиваемых системах)

2. Ограничить максимальный размер массива (например, не более 1Мб)

3. Добавить обработку сигнала SIGSEGV для диагностики ошибок

4. Использовать 64-битные типы для индексов (size_t вместо int)

-->

6. Альтернативные методы ввода

Помимо scanf(), для заполнения массива можно использовать:

  1. Чтение из файла: Подходит для тестирования с большими наборами данных.
    FILE *file = fopen("input.txt", "r");
    

    for (int i = 0; i <n; i++) {

    fscanf(file, "%d", &arr[i]);

    }

    fclose(file);

  2. Генерация случайных чисел: Для тестирования алгоритмов.
    #include <stdlib.h>
    

    #include <time.h>

    srand(time(0));

    for (int i = 0; i <n; i++) {

    arr[i] = rand() % 100; // Числа от 0 до 99

    }

  3. Аргументы командной строки: Для передачи данных при запуске программы.
    int main(int argc, char *argv[]) {
    

    for (int i = 1; i <argc; i++) {

    arr[i-1] = atoi(argv[i]);

    }

    }

Типичные ошибки и их решения

Разберём 3 самых распространённых проблемы при вводе массива:

⚠️ Внимание: Если после ввода массива программа пропускает следующий scanf(), причина в оставшемся символе новой строки (\n) в буфере. Решение: добавьте getchar() после цикла ввода или используйте fgets().
Ошибка Причина Решение
Программа зависает после ввода Бесконечный цикл из-за некорректного scanf() Проверяйте возвращаемое значение scanf()
Выводятся "мусорные" значения Массив не инициализирован или выход за границы Используйте calloc() или проверяйте индексы
Ошибка сегментации (Segmentation fault) Обращение к невыделенной памяти Проверяйте результат malloc() на NULL

FAQ: Ответы на частые вопросы

Можно ли вводить массив символов (строку) с клавиатуры?

Да, для этого используйте fgets() или scanf() с ограничителем длины:

char str[100];

printf("Введите строку: ");

fgets(str, sizeof(str), stdin); // Безопасный ввод

// Или с scanf (менее безопасно):

scanf("%99s", str); // Максимум 99 символов + нулевой байт

Как вводить массив структур с клавиатуры?

Объявите массив структур и заполняйте поля в цикле:

typedef struct {

int id;

char name[50];

} Person;

Person people[5];

for (int i = 0; i <5; i++) {

printf("Введите ID и имя: ");

scanf("%d %49s", &people[i].id, people[i].name);

}

Почему после ввода массива не работает следующий scanf?

Это классическая проблема с оставшимся символом новой строки. Решения:

  1. Добавьте getchar() после цикла ввода
  2. Используйте fgets() + sscanf() вместо scanf()
  3. Очищайте буфер явным образом: while (getchar() != '\n');
Как вводить массив вещественных чисел (float/double)?

Замените %d на %f для float или %lf для double:

double arr[10];

for (int i = 0; i <10; i++) {

scanf("%lf", &arr[i]); // Для double

}

⚠️ При вводе дробных чисел через запятую (например, "3,14") замените запятую на точку или настройте локаль с помощью setlocale(LC_ALL, "Russian").

Можно ли вводить массив без циклов?

Да, но это неудобно для больших массивов. Примеры:

// Для статического массива

int arr[3];

scanf("%d %d %d", &arr[0], &arr[1], &arr[2]);

// Для динамического (требует знания размера заранее)

int arr = malloc(3 sizeof(int));

scanf("%d %d %d", arr, arr+1, arr+2);

Такой подход подходит только для массивов с заранее известным небольшим размером.