Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
http.doc
Скачиваний:
1
Добавлен:
01.05.2025
Размер:
1.34 Mб
Скачать

Удаление файлов

Функция remove() удаляет файлы. Она имеет следующий прототип:

int remove(const char *имя_файла);

В случае удачного выполнения она возвращает ноль, а в случае неудачного - не ноль.

Следующая программа использует remove() для удаления файла, определяемого пользователем:

/* пример использования remove() */ #include <stdio.h> int main(void) { char fname[80]; printf ("Name of file to remove: "); gets(fname); if(remove(fname)) { printf("Error removing file"); return 1; } else return 0; }

ferror() и rewind()

Функция ferror() используется для определения, привела ли выполненная операция к ошибке. Функция ferror() имеет следующий прототип:

int ferror(FILE *fp) где fp - это допустимый указатель на файл. Она возвращает истину, если в результате выполне­ния последней операции произошла ошибка. В противном случае она возвращает ложь. Посколь­ку каждая файловая операция изменяет состояние ошибки, ferror() должна вызываться после каждой файловой операции, иначе ошибка может потеряться.

Функция rewind() сбрасывает маркер файла на начало для файла, указанного в аргументе. Она имеет прототип:

void rewind(FILE *fp) где fp - это допустимый файловый указатель.

Работа с консолью

Независимо от того, когда программа начнет выполне­ние, всегда открыты 5 потоков. Это stdin, stdout, stderr, stdaux и stdprn. Поскольку это файловые указатели, они могут использоваться системой ввода/вывода ANSI С, использующей указатели. Например, функция putchar() может быть определена следующим образом: int putchar(int с) { return putc(c, stdout); }

Как показывает данный пример, С проводит не очень заметную границу между консольным и файловым вводом/выводом. В принципе функции консольного ввода/вывода являются версиями функций для работы с файлами с тем отличием, что ввод или вывод неявно направляется на консоль. Единственная причина их существования - это удобство. В целом можно использовать stdin, stdout и stderr в качестве файловых указателей в функциях, использующих в качестве пара­метра переменную типа FILE *.

В системах, где разрешается перенаправление ввода/вывода, stdin и stdout можно перенаправ­лять. Это означает, что они могут быть связаны с устройством, отличным от экрана или клавиа­туры. Например, рассмотрим программу: #include <stdio.h> int main(void) { char str[80]; printf("Enter a string: "); gets(str); printf(str); return 0; }

Предположим, что данная программа называется TEST. При обычном выполнении TEST она выводит на экран подсказку, читает с клавиатуры строку и выводит данную строку на экран. Как stdin, так и stdout могут быть перенаправлены в файл. Например

TEST > OUTPUT вывод программы TEST будет происходить в файл OUTPUT. Выполнение команды

TEST < INPUT > OUTPUT приведет к тому, что программа будет читать из файла INPUT, а записывать в файл OUTPUT.

Как можно видеть, консольный ввод/вывод и файловый ввод/вывод - это два немного разли­чающихся взгляда на одну и ту же вещь.

Препроцессор и комментарии

Исходный код программы на С (или С++) может содержать различные инструкции компилято­ру. Не являясь частью языка, директивы препроцессора расширяют область применения языка. В данной главе рассматривается препроцессор. Также рассматриваются стандартные макросы и некоторые дополнения, внесенные в препроцессор фирмой Borland.

Стандарт ANSI С определяет следующие директивы препроцессора: #if #ifdef #ifndef #else #elif #endif #include #define #undef #line #error #pragma

Все директивы препроцессора начинаются со значка #, и каждая директива должна находиться в своей собственной строке. Например: /* не будет работать */ #include <stdio.h> #include <stdlib.h> не будет работать.

#define

Директива #define определяет идентификатор и последовательность символов, которой будет за­мещаться данный идентификатор при его обнаружении в тексте программы. Идентификатор так­же называется именем макроса, а процесс замещения называется подстановкой макроса. Стандар­тный вид директивы следующий:

#define имя_макроса последовательность_символов Обратим внимание, что в данном операторе отсутствует точка с запятой. Между идентификато­ром и последовательностью символов может быть любое число пробелов. Макрос завершается только переходом на новую строку.

Например, если необходимо использовать TRUE для значения 1, a FALSE для 0 то можно объявить следующие два макроса: #define TRUE 1 #define FALSE 0 В результате, если компилятор обнаружит в тексте программы TRUE или FALSE, то он заменит их на 1 и 0 соответственно. Например, следующая строка выводит на экран «0 1 2»: printf ("%d %d %d", FALSE, TRUE, TRUE + 1);

В случае, если макрос определен, он может использоваться для определения других макросов. Например, следующий код сопоставляет с именами ONE, TWO и THREE их численные значения: #define ONE 1 #define TWO ONE + ONE #def ine THREE ONE + TWO

В результате макроподстановки идентификаторы замещаются указанными строками. Если не­обходимо определить стандартное сообщение об ошибке, то можно написать что-то вроде следу­ющего: #define E_MS "Standart error on input.\n" /*...*/ printf(E_MS);

Если компилятор обнаруживает идентификатор E_MS, то он замещает его строкой «Standart error on input.» На самом деле компилятор увидит оператор в виде printf("Standart error on input.\n"); Если идентификатор находится в строке, то подстановка не происходит. Например: #define XYZ this is a test /*...*/ printf("XYZ"); выведет не «this is a test», a «XYZ».

Если строка не вмещается в одной строке, то ее можно продолжить на следующей строке, поместив в конце строки обратный слэш, как показано в следующем примере: #define LONG_STRING "This is a very long" \ string that is used as an example." Программисты, пишущие на С, часто используют заглавные буквы для определения идентифика­торов. Данное соглашение помогает любому человеку, читающему программу, бросив на нее один взгляд, узнать, что он имеет дело с макросом. Также вce #define лучше помещать в начале файла или вообще в отдельный заголовочный файл.

Очень часто макросы используют для определения «магических чисел», используемых в про­грамме. Например, программа может определять массив и иметь несколько процедур для работы с ним. Вместо того, чтобы жестко кодировать размер массива, лучше определить макрос, соответ­ствующий размеру массива, и использовать его в тех местах, где необходимо использование раз­мера. Таким образом, если необходимо изменить размер массива, единственное, что требуется сделать, — это изменить оператор #define и перекомпилировать программу. Везде, где использо­вался данный макрос, произойдут автоматические изменения. Рассмотрим пример: #define MAX_SIZE 100 /*...*/ float balance[MAX_SIZE]; /*...*/ float temp[MAX_SIZE]; Для изменения размеров обоих массивов просто изменим определение MAX_SIZE.

Директива #define имеет еще одну возможность: макрос может иметь аргументы. Каждый раз при встрече такого макроса аргументы макроса будут замещаться реальными аргументами про­граммы. Такой тип макроса называется макрос типа функция. Например: #include <stdio.h> #define MIN(a,b) ((a)<(b)) ? (a) : (b) int main(void) { int x, y; x = 10; у = 20; printf("The minimum is: %d", MIN(x, y) ); return 0; } При компиляции программы вместо MIN(a, b) подставляется указанное выражение, причем вме­сто фиктивных параметров а и b подставляются реальные х и у. Таким образом, в результате подстановки функция printf() примет следующий вид: printf ("The minimum is: %d",((x) < (y) ) ? (x) : (y) );

Надо быть осторожным при определении макросов, получающих аргументы, или можно полу­чить несколько неожиданные результаты. Например, рассмотрим следующую короткую програм­му, использующую макрос для определения четности значения:

/* программа выдает неправильный результат */ #include <stdio.h> #define EVEN(a) a%2==0 ? 1 : 0 int main(void) { if (EVEN(9+1) ) printf("is even"); else printf ("is odd"); return 0; } Из-за способа подстановки данная программа работает неправильно. В результате компиляции программы EVEN(9 + 1) расширится до 9 + 1% 2 == 0 ? 1 : 0

Как известно, оператор взятия по модулю имеет более высокий приоритет, чем оператор сло­жения. Это означает, что сначала выполнится взятие по модулю с числом 1, а затем результат прибавится к 9, что, естественно, не может быть равно 0. Для устранения данной проблемы сле­дует заключить а в макросе EVEN в круглые скобки, как показано в следующей правильной вер­сии программы: #include <stdio.h> #define EVEN(a) (a)%2==0 ? 1 : 0 int main(void) { if(EVEN(9 + 1) ) printf("is even"); else printf("is odd"); return 0; } Обратим внимание, что 9+1 вычисляется до взятия по модулю. В целом заключение параметров макроса в скобки — это достаточно хорошая идея, и она позволяет избежать множества проблем. Использование макроподстановок вместо реальных функций имеет одно большое преимуще­ство — существенно увеличивается скорость работы программы, поскольку нет необходимости тратить время на вызов функции и возврат из нее. Тем не менее, за данное увеличение скорости работы следует платить увеличением размера исполнимого кода программы, поскольку програм­ма вынуждена дублировать код макроса.

#error

Директива #error указывает компилятору в случае ее обнаружения остановить компиляцию. Как правило, она используется для отладки. Общий вид директивы следующий: #error сообщение_об_ошибке сообщение_об_ошибке не заключается в двойные кавычки. Когда компилятор обнаруживает директиву, он выводит сообщение в следующем виде и завершает компиляцию: Fatal: имя_файла номер_строки: Error directive: сообщение_об_ошибке Здесь имя_файла — это имя файла, где была найдена директива #error, номер_строки — это номер строки директивы, а сообщение_об_ошибке — это собственно само сообщение.

#include

Директива #include предлагает компилятору включить другой исходный файл, имя которого ука­зывается после директивы. Имя файла заключается в двойные кавычки или в <>. Например, сле­дующие две директивы указывают компилятору на необходимость подключить заголовочный файл стандартной библиотеки ввода/вывода: #include "stdio.h" #include <stdio.h>

Подключаемые файлы также могут иметь директивы #include. Если это имеет место, то говорят о вложенных подключениях. Например, следующая программа подключает файл, который, в свою очередь, подключает другой файл:

/* файл программы */ #include <stdio.h> int main(void) { #include "one" return 0; }

/* подключаемый файл ONE */ printf("This is from the first include file.\n"); #include "two"

/* подключаемый файл TWO */ printf("This is from the second include file.\n");

Если подключаемый файл указан в <>, то поиск будет происходить в стандартных каталогах, предназначенных для хранения заголовочных файлов. В случае, если подключаемый файл заклю­чен в двойные кавычки, поиск будет происходить в текущем рабочем каталоге. Если файл не найден, то поиск продолжается в стандартных каталогах.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]