Вход/Регистрация
Программирование. Принципы и практика использования C++ Исправленное издание
вернуться

Страуструп Бьерн

Шрифт:

Интересен эффект последнего вызова функции 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")
!

Итак, как же безопасно ввести символы? Мы можем использовать вид формата %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++ от этих проблем не страдают.

  • Читать дальше
  • 1
  • ...
  • 453
  • 454
  • 455
  • 456
  • 457
  • 458
  • 459
  • 460
  • 461
  • 462
  • 463
  • ...

Ебукер (ebooker) – онлайн-библиотека на русском языке. Книги доступны онлайн, без утомительной регистрации. Огромный выбор и удобный дизайн, позволяющий читать без проблем. Добавляйте сайт в закладки! Все произведения загружаются пользователями: если считаете, что ваши авторские права нарушены – используйте форму обратной связи.

Полезные ссылки

  • Моя полка

Контакты

  • chitat.ebooker@gmail.com

Подпишитесь на рассылку: