Такие объявления часто можно встретить в старых программах. Вместо этого объявления следует размещать в нескольких строках, используя свободное место для комментариев и инициализации.
char c = 'a'; /* символ завершения ввода для функции f */
char* p = 0; /* последний символ, считанный функцией f */
char a[177]; /* буфер ввода */
char* f; /* считывает данные в буфер a;
возвращает указатель
на первый считанный символ */
Кроме того, выбирайте осмысленные имена.
27.6. Ввод-вывод: заголовок stdio
В языке С нет потоков ввода-вывода iostream
, поэтому мы используем стандартный механизм ввода-вывода языка С, определенный в заголовочном файле
. Эквивалентами потоков ввода и вывода cin
и cout
из языка С++ в языке С являются потоки stdin
и stdout
. Стандартные средства ввода-вывода языка С и потоки iostream
могут одновременно использоваться в одной и той же программе (для одних и тех же потоков ввода-вывода), но мы не рекомендуем это делать. Если вам необходимо совместно использовать эти механизмы, хорошенько разберитесь в них (обратите особое внимание на функцию ios_base::sync_with_stdio
), используя хороший учебник. См. также раздел Б.10.
27.6.1. Вывод
Наиболее популярной и полезной функцией библиотеки stdio
является функция printf
. Основным предназначением функции printf
является вывод С-строки.
#include
void f(const char* p)
{
printf("Hello, World!\n");
printf(p);
}
Это не очень интересно. Намного интереснее то, что функция printf
может получать любое количество аргументов и начальную управляющую строку, которая определяет, как вывести дополнительные аргументы. Объявление функции printf
в языке C выглядит следующим образом:
int printf(const char* format, ...);
Многоточие (...
) означает “и, возможно, остальные аргументы”. Мы можем вызвать функцию printf
так:
void f1(double d, char* s, int i, char ch)
{
printf("double %g string %s int %d char %c\n", d, s, i, ch);
}
где символы %g
означают: “Напечатать число с плавающей точкой, используя универсальный формат”, символы %s
означают: “Напечатать С-строку”, символы %d
означают: “Напечатать целое число, используя десятичные цифры,” а символы %c
означают: “Напечатать символ”. Каждый такой спецификатор формата связан со следующим, до поры до времени не используемым аргументом, так что спецификатор %g
выводит на экран значение переменной d
; %s
— значение переменной s
, %d
— значение переменной i
, а %c
— значение переменной ch
. Полный список форматов функции printf
приведен в разделе Б.10.2.
printf
не является безопасной с точки зрения типов. Рассмотрим пример.
char a[] = { 'a', 'b' }; /* нет завершающего нуля */
void f2(char* s, int i)
{
printf("goof %s\n", i); /* неперехваченная ошибка */
printf("goof %d: %s\n", i); /* неперехваченная ошибка */
printf("goof %s\n", a); /* неперехваченная ошибка */}
Интересен эффект последнего вызова функции printf: она выводит на экран каждый байт участка памяти, следующего за элементом a[1], пока не встретится нуль. Такой вывод может состоять из довольно большого количества символов.
Недостаток проверки типов является одной из причин, по которым мы предпочитаем потоки iostream
, несмотря на то, что стандартный механизм ввода-вывода, описанный в библиотеке stdio
языков C и C++, работает одинаково. Другой причиной является то, что функции из библиотеки stdio
не допускают расширения: мы не можем расширить функцию printf
так, чтобы она выводила на экран значения переменных вашего собственного типа. Для этого можно использовать потоки iostream
. Например, нет никакого способа, который позволил бы вам определить свой собственный спецификатор формата %Y
для вывода структуры struct Y
.
Существует полезная версия функции printf
, принимающая в качестве первого аргумента дескриптор файла.
int fprintf(FILE* stream, const char* format, ...);
Рассмотрим пример.
fprintf(stdout,"Hello, World!\n"); // идентично
// printf("Hello,World!\n");
FILE* ff = fopen("My_file","w"); // открывает файл My_file
// для записи