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++ от этих проблем не страдают.
string s;
cin >> s; // считываем слово
getline(cin,s); // считываем строку
27.6.3. Файлы
В языке C (и C++) файлы можно открыть с помощью функции fopen()
, а закрыть — с помощью функции fclose()
. Эти функции, вместе с представлением дескриптора файлов FILE
и макросом EOF
(конец файла), описаны в заголовочном файле
.
FILE *fopen(const char* filename, const char* mode);
int fclose(FILE *stream);
По существу, мы используем файлы примерно так:
void f(const char* fn, const char* fn2)
{
FILE* fi = fopen(fn, "r"); /* открываем файл fn для чтения */
FILE* fo = fopen(fn2, "w"); /* открываем файл fn для записи */
if (fi == 0) error("невозможно открыть файл для ввода");
if (fo == 0) error("невозможно открыть файл для вывода");
/* чтение из файла с помощью функций ввода из библиотеки stdio,
например, getc() */
/* запись в файл с помощью функций вывода из библиотеки stdio,
например, fprintf() */
fclose(fo);
fclose(fi);
}
Учтите: в языке С нет исключений, потому вы не можете узнать, что при обнаружении ошибок файлы были закрыты.
27.7. Константы и макросы
В языке С константы не являются статическими.
const int max = 30;
const int x; /* неинициализированная константа: OK в C
(ошибка в C++) */
void f(int v)
{