Символ, следующий за знаком %
управляет обработкой аргументов. Первый знак %
применяется к первому дополнительному аргументу (в данном примере спецификатор %d
применяется к переменной x
), второй знак %
относится ко второму дополнительному аргументу (в данном примере спецификатор %s
применяется к переменной p
) и т.д. В частности, рассмотренный выше вызов функции printf()
приводит к следующему результату:
Значение x равно '5', а значение p равно 'asdf'
Затем происходит переход на новую строку.
В принципе соответствие между директивой преобразования %
и типом, к которому она применяется, проверить невозможно. Рассмотрим пример.
printf("Значение x равно '%s', а значение p равно '%d'\n",x,p); // ой!
Набор спецификаторов преобразования довольно велик и обеспечивает большую гибкость (а также много возможностей сделать ошибку). За символом %
могут следовать спецификаторы, описанные ниже.
Нулевая или слишком маленькая ширина поля никогда не приводит к усечению вывода; дополнение вывода нулями или пробелами производится только тогда, когда заданная ширина поля превышает реальную.
Поскольку в языке C нет пользовательских типов в смысле языка C++, в нем нет возможностей для определения форматов вывода для таких классов, как complex
, vector
или string
.
Стандартный поток вывода stdout
в языке C соответствует потоку cout
. Стандартный поток ввода stdin
в языке С соответствует потоку cin
. Стандартный поток сообщений об ошибках stderr
в языке С соответствует потоку cerr
. Эти соответствия между стандартными потоками ввода-вывода в языке C и C++ настолько близки, что потоки ввода-вывода как в стиле языка С, так и стиле языка С++ могут использовать один и тот ж буфер. Например, для создания одного и того же потока вывода можно использовать комбинацию операций над объектами cout
и stdout
(такая ситуация часто встречается в смешанном коде, написанном на языка С и С++). Эта гибкость требует затрат. Для того чтобы получить более высокую производительность, не смешивайте операции с потоками из библиотек stdio
и iostream
при работе с одним и тем же потоком, вместо этого вызывайте функцию ios_base::sync_with_stdio(false)
перед выполнением первой операции ввода-вывода. В библиотеке stdio
определена функция scanf()
, т.е. операция ввода, похожая на функцию printf()
. Рассмотрим пример.
int x;
char s[buf_size];
int i = scanf("Значение x равно '%d', а значение s равно '%s'\n",&x,s);
Здесь функция scanf()
пытается считать целое число в переменную x
и последовательность символов, не являющихся разделителями, в массив s
. Неформатные символы указывают, что они должны содержаться в строке ввода. Рассмотрим пример.
"Значение x равно '123', а значение s равно 'string '\n"
Программа введет число 123
в переменную x
и строку "string
", за которой следует 0
, в массив s
. Если вызов функции scanf()
завершает работу успешно, результирующее значение (i
в предыдущем вызове) будет равно количеству присвоенных аргументов-указателей (в данном примере это число равно 2
); в противном случае оно равно EOF
. Этот способ индикации ввода уязвим для ошибок (например, что произойдет, если вы забудете вставить пробел после строки "string
" в строке ввода?). Все аргументы функции scanf()
должны быть указателями. Мы настоятельно рекомендуем не использовать эту функцию.
Как же ввести данные, если мы вынуждены использовать библиотеку stdio
? Один и из распространенных ответов гласит: “Используйте стандартную библиотечную функцию gets()
”.
// очень опасный код:
char s[buf_size];
char* p = gets(s); // считывает строку в массив s
Вызов p=gets(s)
будет вводить символы в массив s
, пока не обнаружится символ перехода на новую строку или не будет достигнут конец файла. В этом случае в конец строки s
после последнего символа будет вставлен 0
. Если обнаружен конец файла или возникла ошибка, то указатель p устанавливается равным NULL
(т.е. 0
); в противном случае он устанавливается равным s
. Никогда не используйте функцию gets(s)
или ее эквивалент scanf("%s",s))!
За прошедшие годы создатели вирусов облюбовали их слабые места: генерируя вводную строку, переполняющую буфер ввода (в данном примере строку s
), они научились взламывать программы и атаковать компьютеры. Функция sprintf()
страдает от таких же проблем, связанных с переполнением буфера.
Библиотека stdio
содержит также простые и полезные функции чтения и записи символов.