Ввод-вывод в C: stdio.h, потоки stdin/stdout/stderr, функции getchar/putchar, gets/puts, scanf/printf
Концепция потоков (streams)
В C все устройства ввода-вывода представлены как потоки байтов. Три стандартных потока открываются автоматически при запуске программы:
| Поток | Файловый указатель | Устройство по умолчанию | Назначение |
|---|---|---|---|
| Стандартный ввод | stdin (FILE*) | Клавиатура | Чтение данных |
| Стандартный вывод | stdout | Экран (терминал) | Вывод результатов |
| Стандартный вывод ошибок | stderr | Экран (терминал) | Вывод диагностики/ошибок |
stdin, stdout, stderr — макросы, расширяющиеся в выражения типа FILE*. Определены в <stdio.h>.
Буферизация
- Полная буферизация (fully buffered): данные накапливаются в буфере до заполнения (обычно 4-8 КБ). Файлы на диске.
- Построчная буферизация (line buffered): сброс при
\n.stdoutв терминале. - Без буферизации (unbuffered): каждый символ сразу.
stderr.
Управление: setbuf(), setvbuf(), fflush() (принудительный сброс).
Симвловой ввод-вывод
int getchar(void)
Читает один символ из stdin. Возвращает int (символ как unsigned char или EOF — обычно -1).
int c;
while ((c = getchar()) != EOF) {
putchar(c); // эхо
}
int putchar(int c)
Пишет символ (младший байт c) в stdout. Возвращает записанный символ или EOF при ошибке.
Строчный ввод-вывод
char *fgets(char *s, int n, FILE *stream) — РЕКОМЕНДУЕТСЯ
Читает до n-1 символов или до \n (включительно) или EOF. Добавляет \0. Возвращает s или NULL (EOF/ошибка).
char line[256];
while (fgets(line, sizeof(line), stdin)) {
// line содержит \n в конце (если поместилось)
fputs(line, stdout);
}
int fputs(const char *s, FILE *stream)
Пишет строку s (без автоматического \n) в поток. Возвращает неотрицательное при успехе, EOF при ошибке.
gets() — УДАЛЕНА В C11 (ОПАСНА!)
Не проверяет границы буфера → переполнение. Никогда не используйте. Замените на fgets().
Форматированный ввод-вывод: scanf / printf
printf — форматированный вывод
int printf(const char *format, ...);
int fprintf(FILE *stream, const char *format, ...);
int sprintf(char *buf, const char *format, ...);
int snprintf(char *buf, size_t n, const char *format, ...); // БЕЗОПАСНЫЙ!
Основные спецификаторы формата
| Спецификатор | Тип аргумента | Вывод |
|---|---|---|
%d, %i | int | Знаковое десятичное |
%u | unsigned int | Беззнаковое десятичное |
%ld, %lld | long, long long | Длинные знаковые |
%zu | size_t | Размер (беззнаковый) |
%f | double (float промоутится) | Фиксированная точка: 123.456 |
%e, %E | double | Научная: 1.23e+02 |
%g, %G | double | Кратчайшее из %f/%e |
%c | int (char) | Символ |
%s | const char* | Строка (до \0) |
%p | void* | Указатель (hex) |
%% | — | Литерал % |
Флаги, ширина, точность
%10d // ширина 10, выравнивание вправо
%-10d // ширина 10, выравнивание влево
%010d // ширина 10, заполнение нулями
%.2f // 2 знака после запятой
%10.2f // ширина 10, 2 знака после запятой
%.*f // точность из аргумента: printf("%.*f", 3, 1.23456) → "1.235"
%*d // ширина из аргумента
scanf — форматированный ввод
int scanf(const char *format, ...);
int fscanf(FILE *stream, const char *format, ...);
int sscanf(const char *str, const char *format, ...);
Важные правила scanf
- Аргументы — указатели:
scanf("%d", &x). - Пропускает whitespace (пробелы, табы, \n) перед
%d,%f,%s... НЕ пропускает перед%c,%[...],%n. %sчитает до whitespace! Не читает строку с пробелами. Используйте%99[^\n]илиfgets.- Всегда проверяйте возвращаемое значение (число успешно прочитанных элементов).
- Безопасность: для
%sвсегда указывайте ширину:%99s(буфер 100).
Примеры scanf
int a; float b; char name[50];
scanf("%d %f", &a, &b); // два числа
scanf("%49s", name); // слово (до пробела), макс 49 символов
scanf(" %49[^\n]", name); // строка до \n (пробел перед % съедает \n от прошлого ввода)
int n; scanf("%d%n", &a, &n); // %n — записывает число прочитанных символов
Бинарный ввод-вывод
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
Читают/пишут блоки памяти (массивы, структуры). Возвращают число прочитанных/записанных элементов.
struct Record { int id; double val; } rec;
fwrite(&rec, sizeof(rec), 1, fp); // запись структуры
fread(&rec, sizeof(rec), 1, fp); // чтение
Внимание: бинарные файлы непереносимы (endianness, padding, размер типов). Для переносимости используйте сериализацию (JSON, protobuf, свой формат).
Работа с файлами (кратко, подробнее в следующей статье)
FILE *fp = fopen("file.txt", "r"); // "r", "w", "a", "r+", "w+", "a+", "rb", "wb"...
if (!fp) { perror("fopen"); return 1; }
// чтение/запись через fgetc, fgets, fscanf, fprintf, fread, fwrite...
fclose(fp); // обязательно! сбрасывает буферы, закрывает дескриптор
Обработка ошибок
- Проверяйте возвращаемые значения всех функций ввода-вывода.
ferror(fp)— флаг ошибки потока.feof(fp)— флаг EOF (устанавливается после неудачной попытки чтения ПОСЛЕ конца).clearerr(fp)— сброс флагов.perror("msg")— печатаетmsg: системное_сообщение_об_ошибкевstderr.errno(из<errno.h>) — код последней ошибки.
Пример: копирование файла с буферизацией
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
if (argc != 3) {
fprintf(stderr, "Usage: %s \n", argv[0]);
return 1;
}
FILE *in = fopen(argv[1], "rb");
if (!in) { perror(argv[1]); return 1; }
FILE *out = fopen(argv[2], "wb");
if (!out) { perror(argv[2]); fclose(in); return 1; }
char buf[4096];
size_t n;
while ((n = fread(buf, 1, sizeof(buf), in)) > 0) {
if (fwrite(buf, 1, n, out) != n) {
perror("write error");
break;
}
}
if (ferror(in)) perror("read error");
fclose(in);
fclose(out);
return 0;
}
Язык C
- Понимание ключевых слов и идентификаторов C#: правила, списки и лучшие практики
- Основы ввода-вывода в C#: Console.WriteLine, ReadLine и форматирование
- Классы и объекты в C#: Фундаменты надёжного ООП
- Освоение ввода и вывода в C++: практическое руководство
- Ввод-вывод в C: scanf() и printf() — подробное руководство
- Мастерство ввода-вывода и импорта модулей в Python: Практическое руководство
- Ввод и вывод в Java: печать, чтение и форматирование данных
- Потоки в C++: cout, cin, cerr, clog — практическое руководство
- Константы и литералы в C: целочисленные, вещественные, символьные, строковые и определение констант
- Константы и литералы в C#: типы, правила и лучшие практики