Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ЗФ_ОАиП / Лекции ГГУ Скорины - Программирование.doc
Скачиваний:
179
Добавлен:
21.03.2016
Размер:
2.27 Mб
Скачать

27.4. Функция двоичного поиска – bsearch()

Двоичный поиск (дихотомия) – это быстрый поиск элемента в отсортированном множестве данных. Для двоичного поиска в стандартной библиотеке языка С есть функция bsearch(), причем она позволяет выполнять поиск элементов в массиве данных произвольного типа (как базового так и пользовательского). Описание функции находится в файле <stdlib.h>.

Прототип функции двоичного поиска:

void *bsearch(const void *key, const void *base,

unsigned int n, unsigned int size,

int (*cmp)(const void*, const void*));

Функция осуществляет поиск в массиве и возвращает адрес первого элемента, который соответствует шаблону поиска. Если соответствие не найдено, то функция возвращает значение NULL. Так как bsearch() выполняет двоичный поиск, то первый соответствующий элемент не обязательно будет первым таким элементом в массиве.

Описание параметров: key – адрес переменной, значение которой надо найти в массиве, base – указатель на начало массива для поиска, n – число элементов в массиве, size – размер в байтах одного элемента массива.

Параметр int (*cmp)(const void*, const void*) указатель на функцию сравнения, которая показывает, какой из двух сравниваемых элементов больше. Подробное описание такой функции приведено при описании функции быстрой сортировки qsort().

Задача. Пример поиска в одномерном и двумерном массивах целых чисел.

#include <stdlib.h> // для функций qsort() и bsearch()

#include <stdio.h>

int cmp (const void *p1, const void *p2) {

return *(int *)p1-*(int*)p2;

}

void main(){

int *ptrFind; // адрес найденного элемента в массиве

int find; // для поиска

int i;

int a[3][3] = { {5, 0, 12},

{1, 20, 9},

{17, 3, 7} };

int m[10] = {11, 3, 9, 4, 0, 15, 19, 18, 12, 7};

// сортируем двумерный массив, считая его одномерным из 9 элементов

// результат сортировки: {0, 1, 3},

// {5, 7, 9},

// {12, 17, 20}

qsort(a, 9, sizeof(a[0][0]),cmp);

// сортируем одномерный массив

qsort(m, 10, sizeof(m[0]),cmp);

for (i=0; i<5; i++) {

printf("find = ");

scanf("%d", &find);

ptrFind = (int *)bsearch(&find,a,9,sizeof(int),cmp);

if (ptrFind != NULL)

printf("%d есть в двумерном массиве a\n", find);

else

printf("%d нет в двумерном массиве a\n", find);

ptrFind = (int *)bsearch(&find,m,10,sizeof(int),cmp);

if (ptrFind != NULL)

printf("%d есть в одномерном массиве m", find);

else

printf("%d нет в одномерном массиве m", find);

}

}

28. Работа с файлами

28.1. Основные понятия

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

Если в качестве устройств ввода/вывода ограничиться только клавиатурой и экраном, то в таком случае будет сложно обработать большие объемы входных данных. Выходные данные, отображенные на экране, тоже безвозвратно теряются. Для устранения подобных проблем удобно сохранять данные на запоминающих устройствах, предназначенных для долговременного хранения данных (диски, флэшки и т.п.).

На дисках данные хранятся в виде структур данных, обслуживаемых операционной системой, – в виде файлов. Файл – это именованная область внешней памяти, в которой хранится логически завершенный объем данных. В языке С файл – это неструктурированная линейная последовательность байтов. Файлы широко применяются при решении различных задач. Каждый файл имеет имя и расширение, которого может и не быть (в MS DOS максимальная длина соответственно: 8 и 3 символа).

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

Функции ввода/вывода для работы с файлами стандартной библиотеки языка С, которые позволяют читать данные из файлов и записывать данные в файлы, делятся на два класса:

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

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

Для пользователя файл, открытый на верхнем уровне, представляется как последовательность считываемых или записываемых байтов. Чтобы отразить эту особенность организации ввода/вывода, предложено понятие «поток» (англ. stream). Когда файл открывается, с ним связывается поток. Далее выводимая информация записывается «в поток», а считываемая – берется «из потока».

Когда файл открывается для ввода/вывода, он связывается со структурой типа FILE, определенной с помощью typedef в файле <stdio.h>. Эта структура содержит всю необходимую информацию о файле (его имя, открыт ли файл для чтения или записи, указатель текущей позиции в файле, адрес буфера, были ли ошибки при работе с файлом и не встретился ли конец файла). При открытии файла с помощью стандартной функции fopen() возвращается указатель на структуру типа FILE. Этот указатель, называемый указателем потока (файла), используется для последующих операций с файлом. Его значение передается всем библиотечным функциям, используемым для ввода/вывода через этот поток.

2. Ввод/вывод низкого уровня (с использованием понятия «дескриптор» или «префикс»). Функции низкого уровня не выполняют буферизацию и форматирование данных, они позволяют непосредственно пользоваться средствами ввода/вывода операционной системы.

При низкоуровневом открытии файла с помощью функции open() с ним связывается файловый дескриптор (англ. handle). Дескриптор является целым числом, характеризующим размещение информации об открытом файле во внутренних таблицах операционной системы (FCB – File Control Block). Дескриптор используется при последующих операциях с файлом.

Файлы делятся на текстовые и двоичные.

Текстовый файл – это файл, в котором каждый символ хранится в виде одного байта (кода, соответствующего символу). Текстовые файлы разбиваются на несколько строк с помощью специального символа «конец строки» (пара символов CR LF или 0Dh 0Ah).

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

Для обоих классов функций файлового ввода-вывода возможны 2 режима доступа к файлу: текстовый и двоичный. Режим доступа к файлу задается при его открытии. В текстовом режиме производится преобразование пары символов CR LF (0Dh 0Ah) – при чтении из файла в один символ новой строки ‘\n’(0xA), а при записи в файл обратно в пару символов. Кроме того, при вводе символа Ctrl-Z (1Ah) считается, что достигнут конец файла и ввести информацию после этого символа в текстовом режиме нельзя. В двоичном режиме никакого преобразования байтов не происходит и не делается никаких предположений об их смысле.

Доступ к файлу может быть последовательным и произвольным (прямым). Последовательный доступ – файл читается/пишется последовательно байт за байтом. Прямой доступ – чтение/запись возможны из произвольного места файла с установкой указателя чтения/записи в нужное место файла.

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