Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Метод_материалы / Учебники / Программирование_С

.pdf
Скачиваний:
66
Добавлен:
16.03.2016
Размер:
2.31 Mб
Скачать

struct Person

{

char * name; int age;

};

Покажем обязательные синтаксические конструкции в прототипах функций, которые работают со структурами:

1)

параметр функции — объект структурного типа:

 

void

F1 (struct Person Имя_объекта);

2)

параметр функции — указатель на объект структурного типа:

 

void

F2 (struct Person * Имя_объекта);

3)

функция возвращает структуру:

 

struct Person F3 (Список_параметров);

4)функция возвращает указатель на структуру: struct Person * F4 (Список_параметров) ;

Механизмы передачи параметров и возвращения значения функцией подробно рассмотрены в первой главе.

1.3.7.Ввод-вывод данных в С

Вязыке С нет средств ввода-вывода. Этим обеспечивается аппаратная независимость языка. Ввод-вывод реализуется посредством библиотек стандартных функций языка С (стандарт ANSI C). Библиотека stdio.h содержит средства ввода-вывода (обмена с устройствами), в том числе с файлами на диске.

Сточки зрения языка нет разницы, с устройством или файлом происходит обмен. Существует три уровня ввода-вывода:

верхнего уровня (потоковый),

записями (низкоуровневый),

для консоли и портов. это системно-зависимый обмен.

Вданном пособии мы рассмотрим только работу с файлами.

1.3.7.1. Файлы

Определение. Файл — именованная область внешней памяти, в которой содержится некоторая информация. Например, тексты программ хранятся в виде текстовых файлов. Файлы также могут хранить данные, а программы могут обращаться к файлам данных для чтения информации или записи.

Файлы удобно использовать, когда программа обрабатывает большой объем информации, или когда данные должны храниться на внешних устройствах, или когда данные могут быть исходными для нескольких программ обработки.

Программы, которые работают с данными, хранящимися в файле, чаще всего, временно размещают их в оперативной памяти. Этот прием позволяет увеличить скорость обработки данных и снизить сложность программ. Данные из файлов,

111

будучи прочитаны программой, становятся значениями объектов программы, например, массивами, матрицами и пр.

1.3.7.2. Типы файлов

По механизму хранения данных и обращения к ним файлы разделяются на файлы последовательного доступа и файлы прямого доступа.

Файлами последовательного доступа являются текстовые файлы. Такие файлы подготавливаются в текстовом редакторе и хранят данные в символьном представлении. Их можно легко просматривать и редактировать

Файл прямого доступа — это двоичный файл. Хранит данные одного типа, не обязательно базового. Каждое данное хранится во внутреннем представлении, размер определен типом данного. Чтобы получить какое-нибудь данное, можно переместить указатель файла непосредственно на это данное, и выполнить операцию обмена.

1.3.7.3. Использование файлов

1.3.7.4.1. Объявление файловой переменной

Для объявления логического имени файла используется тип FILE, описание которого находится в библиотеке stdio.h. Это структура данных, которая хранит данные о файле, такие как размер буфера и прочие.

Объявление файла: FILE *имя_файла;

//имя_файла: имя переменной, связаннойс файлом

//указатель на стандартную структуру данных FILE

Это объявление аналогично объявлению обычных переменных, так же как:

int

*a;

 

FILE

*имя;

 

Например:

 

FILE

*in, *out;

// файл in для ввода, out для вывода

FILE

*my_file; *my_other_file;

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

1.3.7.4.2. Открытие файла Смысл этой операции в том, чтобы связать логическое имя файла с файлом,

физически существующим на диске. Логическому имени файла присваивается значение, возвращаемое функцией fopen. В случае ошибки открытия файла fopen возвращает NULL. Функция имеет два параметра, оба строкового типа.

Формат обращения к функции:

имя_файла = fopen("имя_физического_файла", "режим_открытия_файла"); например:

in = fopen("input.txt", "r"); out = fopen("output.txt", "wb");

112

Имя физического файла — это строка, значение которой определяет имя файла, возможно, с указанием полного пути к нему. Режим открытия, это строка, определяющая режим доступа к потоку и тип файла, содержит один или два символа. Первый символ определяет тип операций обмена.
r — для чтения. Файл должен существовать на диске. Логическое имя файла связывается с физическим именем. Позиция чтения устанавливается перед первым байтом файла.
w — для записи. Если файл существует на диске, то он будет открыт для записи. Позиция записи устанавливается перед первым байтом файла. Если в файле была информация, она будет утеряна. Если файл не существует, он будет создан.
a — для добавления в конец. Если файл существовал на диске, он будет открыт для записи. Позиция записи устанавливается перед признаком конца файла. Если файла не было, он будет создан.
Признак + после типа файла, r+, w+, a+ расширяет возможности операций обмена, модифицируя тип файла. С таким типом файл открывается как для чтения, так и для записи.
r+ — существующий файл открывается как для чтения, так и для записи в любом месте файла. Запись в коней недопустима, так как недопустимо увеличение размера файла.
w+ — новый файл открывается для записи и последующих изменений. Если файл существует, прежнее содержимое стирается. Последующие операции записи и чтения допустимы в любом месте файла, в том числе в конец, то есть размер файла может увеличиваться.
a+ — файл открывается или создается и становится доступным для изменений, то есть для записи и чтения в любом месте файла. В отличие от w+ при открытии существующего файла его содержимое не уничтожается, в отличие от r+ можно добавить запись в конец файла.
Второй символ определяет тип файла, следовательно, тип хранения данных. t — текстовый файл. Действует по умолчанию.
b — двоичный файл.
Физический файл может находиться где угодно. Имя файла можно указывать с полным путем. Если имя краткое, файл будет отыскиваться только в текущем каталоге.
fopen("c:\\work\\f.txt","wt");
Имя физического файла может изменяться, тогда параметр функции fopen должен быть переменной строкового типа:
char *name_of_file = new char [50];
...
puts ("Введите имя файла\n"); gets (name_of_file);
fopen (name_of_file,"wt"); // запишет в файл, который
// укажет пользователь при работе. 113

1.3.7.4.3. Ошибки открытия файла Существуют некоторые обычные ошибки открытия файла, например, файл не

найден, диск заполнен, недостаточно динамической памяти для выполнения операции и прочие. При любой ошибке открытия файла fopen возвращает NULL. Это используется для того, чтобы обрабатывать возможные ошибки вводавывода. В случае возникновения одной из штатных ошибок ввода-вывода, ее код errno распознает и обрабатывает функция perror:

FILE *my_file;

if ((my_file = fopen("f.txt","wt")) = = NULL)// текстовый для записи

{

perror ("Ошибка открытия файла");

//если есть ошибка, ее код = errno

//perror анализирует номер ошибки

//и выводит поясняющий текст

} exit();

Прототип функции perror находится в stdio.h. Там же определена переменная int errno. Ею пользуются многие функции С, в том числе, функции ввода-вывода. fopen, обнаружив ошибку, заносит ее код в переменную errno. Функция perror выводит на экран текстовую строку (свой аргумент), затем двоеточие, пробел и сообщение об ошибке, содержимое и формат которого определены реализацией.

Если файл не найден, а ошибка не анализируется, поток ввода перенаправляется на стандартный поток ввода-вывода (консоль), с попыткой чтения из буфера обмена.

1.2.7.4.4. Закрытие файла Закрытие файла — это высвобождение логического имени файла.

Используется, чтобы отвязать логическое имя от физического файла. Логическое имя не перестает существовать, и может быть использовано повторно для других целей, например, для изменения режима работы с файлом.

Синтаксис:

fclose (имя_файла);

Пример: fclose (in); fclose (out);

При завершении работы программы все открытые в ней файлы будут закрыты, даже если операция закрытия не была выполнена программистом. Если файл закрывает программист, то все данные из буфера выводятся в файл перед его закрытием. Если же файл закрывается при завершении программы, то некоторые данные могут быть потеряны вследствие механизма буферизации обмена.

1.2.7.4.5. Конец файла

Признак конца файла (end-of-file) присутствует в конце файла всегда. Чтобы его распознать в потоке, используется макроопределение feof. С его помощью можно проверить, найден ли в потоке признак конца файла (end-of-file).

114

Объявление:

int feof (FILE *stream);

Как видим, тип возвращаемого значения int, следовательно, данная функция возвращает целое значение (точнее, логическое). feof проверяет данный поток на наличие end-of-file в текущем положении указателя потока. Возвращаемое значение отлично от нуля, если при выполнении последнего оператора ввода для потока найден end-of-file, и равно нулю, если end-of-file не обнаружен.

1.2.7.5. Чтение и запись для текстовых файлов

Все функции ввода-вывода, которые были рассмотрены ранее, работают по принципам потокового ввода-вывода, используя стандартные текстовые потоки stdin, stdout, srderr.

Для символов:

 

int getc ()

// читает символ (int) c клавиатуры stdin

int putc (int C)

// выводит символ C на экран stdout

Для строк:

 

char *gets (char *S)

// читает строку S (char *) c клавиатуры stdin

int puts (char *S)

// выводит строку S на экран stdout

Для форматированных данных:

 

int scanf (const char* format,…)

// читает по форматному вводу из stdin

int printf (const char* format,…)

// выполняет форматный вывод в stdout

При работе с файлами для текстовых файлов используются другие функции библиотеки <stdio.h>, отличительным признаком которых является буква f в начале имени функции. Каждая из них имеет, по сравнению с вышеперечисленными функциями, дополнительный параметр — это логическое имя файла, для которого выполняется данная операция. Остальные механизмы остаются неизменными.

При открытии файлов для ввода и вывода перехват возможной ошибки выполняет условный оператор. Функция perror возвращает сообщение об ошибке, а fprintf переназначает вывод строки сообщения об ошибке в стандартный поток stderr.

Пример. Посимвольное копирование данных из одного файла в другой.

#include <stdio.h>

#include <stdlib.h> void main (void)

{

FILE *in, *out;

// открыть файлы

if ((in = fopen ("prim_in.txt", "rt")) == NULL)

{

perror ("Ошибка открытия файла для ввода.");

fprintf (stderr, "Ошибка открытия файла для ввода.\n"); return; // программа завершит работу

115

}

if ((out = fopen("prim_out.txt", "wt")) == NULL)

{

fprintf (stderr, "Ошибка открытия файла для вывода.\n"); return; // программа завершит работу

}

//циклом копирования управляет макроопределение feof

//условие выхода «пока входной файл не закончился» while ( !feof(in) )

fputc (fgetc (in), out); // вВыводит в out символ, введенный fgetc(in) fclose(in);

fclose(out);

}

1.2.7.6. Чтение и запись для бинарных файлов

Двоичный файл хранит информацию в плотном упакованном двоичном представлении. Данные, хранящиеся в таком файле, могут быть произвольного типа, не обязательно базового. Все они имеют одинаковый размер, определенный размером типа данного, пересылаемого в файл. В качестве аргумента указывается void область памяти, обмен происходит сплошным потоком байт. Для обмена с такими файлами используются те же приемы, кроме операций ввода-вывода. Этот обмен не форматированный, поэтому используются функции fread и fwrite, прототипы которых:

size_t fread (void *buf, size_t size, size_t count, FILE *stream)

Функция прочитывает count элементов размером size байт каждый в область памяти, адрес которой определен указателем buf, из файла, определенного указателем stream. Возвращает количество прочитанных элементов, которое может быть меньше, чем count, если произошла ошибка ввода или встречен конец файла.

size_t fwrite (const void *ptr, size_t size, size_t count, FILE *stream)

Функция записывает count элементов размером size байт каждый из области памяти, адрес которой определен указателем buf, в файл, определенный указателем stream. Возвращает количество записанных элементов.

Упрощенно можно записать синтаксис этих функций так:

fread (Адрес_области_ввода, Размер_объекта, Количество_объектов, Имя_файла);

fwrite (Адрес_области_вывода, Размер_объекта, Количество_объектов, Имя_файла);

Например, для обмена двумерного массива данных типа float размером 10*10, можно использовать одно обращение к функции, в результате которого в поток будут переправлены 10*10*4 = 400 байт:

float

a[10][10];

..

 

fread (a, sizeof (float), 10*10, in);

116

fwrite (a, sizeof (float), 10*10, out);

1.3.8.Конструкции псевдокода

Вэтом пособии не все тексты программ приведены на языке C. Значительная часть алгоритмов записана на уровне псевдокода, который представляет собой некое обобщение кодов различных языков программирования. В этом разделе приведены наиболее часто используемые в пособии конструкции псевдокода.

1.3.8.1.Ветвления — альтернатива и выбор

Альтернатива: if (условие)

действие;

или

if (условие) действие1;

else

действие2;

Выбор:

switch ( переменная)

{

case <константа> : < предложение >; [break;]

default : <предложение>;

}

1. 3.8.2. Блок

{

действие1; действие2;

действие n;

};

1. 3.8.3. Цикл с предусловием while (условие)

действие;

117

1. 3.8.4. Цикл с постусловием do

действие; while (условие);

1. 3.8.5. Цикл с параметром

for (условие для числа повторений) действие;

1. 3.8.6. Функции

Используется синтаксис языка C. В данном пособии псевдокод частично соответствует синтаксису языка C, однако полностью с ним не совпадает.

Условия для ветвлений и циклов мы будем записывать, не придерживаясь каких-либо правил, но в интуитивно понятной форме.

Наконец, заметим, что часто в приведенных ниже описаниях алгоритмов первый индекс массива есть 1 — хотя в языке C, разумеется, это 0. Предопределенные константы FALSE и TRUE — это соответственно 0 и –1, в соответствии с синтаксисом C.

118

2. СТРУКТУРЫ ДАННЫХ КАК ОСНОВА ДЛЯ РАЗРАБОТКИ ПРОГРАММ

2.1.Статические конструируемые типы данных

2.1.1.Стек (на основе массива)

Вэтой главе будет продолжен процесс конструирования новых структур данных из ранее построенных типов. Термин "статические" означает, что эти структуры размещаются в памяти фиксированного, заранее определенного размера (в отличие от изучаемых в главе 2.2 динамических структур).

Первой из таких структур является стек (stack), который можно себе представить в виде стакана, куда можно положить некий объект (на уже лежащие

встеке) и откуда можно достать объект (с самого верха). На рис. 2.1 проиллюстрирована работа со стеком, элементы которого имеют тип int. Сначала стек пуст, затем на его "дно" положен элемент 7, затем поочередно положено еще три элемента, затем верхний элемент взят из стека.

Основные функции:

положить элемент,

взять элемент.

Рис. 2.1. Стек на основе массива

Принцип очередности, реализованный в стеке, называют LIFO — Last In, First Out, то есть "первым зашел — последним вышел". Стрелкой на рис. 2.1 изображен так называемый указатель стека, который является индексом первой свободной ячейки стека (то есть ячейки над вершиной стека).

119

Стек как структура данных поддерживает только две функции для работы с ним: положить — Push ("протолкнуть") элемент и достать — Pop ("вытолкнуть") элемент. Однако для организации корректной работы со стеком нужно еще знать его состояние: не пуст ли он или не полон (в реальной ситуации размер стека ограничен). Поэтому можно ввести дополнительные функции.

Вспомогательные функции:

стек_пуст: да/нет,

стек_полон: да/нет.

Пример программы, реализующей основные функции стека на основе массива:

#include <stdio.h>

#include <conio.h>

# define ST_SIZE 10

// размер стека

# define TRUE –1

 

# define FALSE 0

 

struct STACK {

// структура стека

int

elem[ST_SIZE];

// массив элементов

int

top;

// индекс вершины стека

};

 

 

// прототипы функций стека

 

void ClearStack( STACK * pt_st );

// очистка стека

int StackIsFull( STACK * pt_st );

// проверка заполненности стека

int StackIsEmpty( STACK * pt_st ) ;

// проверка пустоты стека

int Push( STACK * pt_st, int new_el );

// проталкивание элемента в стек

int Pop( STACK * pt_st, int * new_el );

// выталкивание элемента из стека

main()

 

 

{

// создаем экземпляр стека

STACK st_var;

STACK * pt;

// создаем указатель на стек

int st_el;

// целое число — элемент стека

120

Соседние файлы в папке Учебники