Шрифт:
Интересен эффект последнего вызова функции 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
// для записи
fprintf(ff,"Hello, World!\n"); // запись "Hello,World!\n"
// в файл My_file
Дескрипторы файлов описаны в разделе 27.6.3.
27.6.2. Ввод
Ниже перечислены наиболее популярные функции из библиотеки
stdio
.
int scanf(const char* format, ...); /* форматный ввод из потока stdin */
int getchar(void); /* ввод символа из потока stdin */
int getc(FILE* stream); /* ввод символа из потока stream*/
char* gets(char* s); /* ввод символов из потока stdin */
Простейший способ считывания строки символов — использовать функцию
gets
. Рассмотрим пример.
char a[12];
gets(a); /* ввод данных в массив символов a вплоть до символа '\n' */
gets
отравлена. Вместе со своей ближайшей “родственницей” — функцией scanf("%s")
— функция gets
является мишенью для примерно четверти успешных хакерских атак. Она порождает много проблем, связанных с безопасностью. Как в тривиальном примере, приведенном выше, вы можете знать, что до следующей новой строки будет введено не более 11 символов? Вы не можете этого знать. Следовательно, функция gets
почти наверное приведет к повреждению памяти (байтов, находящихся за буфером), а повреждение памяти является основным инструментом для хакерских атак. Не считайте, что можете угадать максимальный размер буфера, достаточный на все случаи жизни. Возможно, что “субъект” на другом конце потока ввода — это программа, не соответствующая вашим критериям разумности. Функция
scanf
считывает данные с помощью формата точно так же, как и функция printf
. Как и функция printf
, она может быть очень удобной.
void f
{
int i;
char c;
double d;
char* s = (char*)malloc(100);
/* считываем данные в переменные, передаваемые как указатели: */
scanf("%i %c %g %s", &i, &c, &d, s);
/* спецификатор %s пропускает первый пробел и прекращает
действие на следующем пробеле */
}
printf
, функция scanf
не является безопасной с точки зрения типов. Форматные символы и аргументы (все указатели) должны точно соответствовать друг другу, иначе во время выполнения программы будут происходить странные вещи. Обратите также внимание на то, что считывание данных в строку s
с помощью спецификатора %s
может привести к переполнению. Никогда не используйте вызовы gets
или scanf("%s")
!
char buf[20];
scanf("%19s",buf);
Нам требуется участок памяти, заканчивающийся нулем (содержание которого вводится функцией
scanf
), поэтому 19 — это максимальное количество символов, которое можно считать в массив buf
. Однако этот способ не отвечает на вопрос, что делать, если некто введет больше 19 символов. Лишние символы останутся в потоке ввода и будут обнаружены при следующей попытке ввода. Проблема с функцией
scanf
означает, что часто благоразумно и легче использовать функцию getchar
. Типичный ввод символов с помощью функции getchar
выглядит следующим образом:
while((x=getchar)!=EOF) {
/* ... */
}
Макрос
EOF
, описанный в библиотеке stdio
, означает “конец файла”; см. также раздел 27.4. Альтернативы функций
scanf("%s")
и gets
в стандартной библиотеке языка C++ от этих проблем не страдают.