книги / Программирование на языке Си
..pdfГлава 8. Примеры разработки программ |
393 |
{
printf("\пОшибка в данных!"); return 1;
}
/* Память для гистограммы: */ gisto = (int*)calloc(K,2); if (gisto==NULL)
{
printf("\n Нет памяти!"); return 1;
}
count( ); mean — sum/N;
variance = (sum2-sum*sum/N)/ (N-l); printf("\n Среднее по выборке MEAN: %f\n",
mean);
printf("Дисперсия по выборке VAR: %f\n", variance);
estimate(fimgist, fivgist, Sdgist); printf("Среднее по гистограмме MGIST: %f\n",
mgist) ;
printf("Дисперсия по гистограмме VGIST: %f\n", vgist);
printf("Дисперсия с поправкой DGIST: %f\n", dgist);
compare(mean, variance); free(gisto);
}
В программе введена защита от неверно вводимых значений N и К, а также проверяется успешность выделения динами ческой памяти для массива гистограммы.
Р ез у ль та ты в ып о л н е н и я п ро г р а м м ы :
N=10000
К=20
Среднее по выборке MEAN: 8176.704700 Дисперсия па выборке VAR: 1867412.218920 Среднее по гистограмме MGIST: 8175.831300
Дисперсия по гистограмме VGIST: 1924404.997440 Дисперсия с поправкой DGIST: 1924336.747440
394 |
|
|
Программирование на языке Си |
|
Сравнительная |
таблица |
|
|
|
j |
<=j |
gisto[j] |
gauss(cj) |
delta |
0 |
409.5 |
0 |
0 |
-2.3e-04 |
1 |
1228.5 |
0 |
0 |
-5.8e-03 |
2 |
2047.5 |
3 |
0 |
2.9e+00 |
3 |
2866.5 |
7 |
1 |
5.7e+00 |
4 |
3685.5 |
27 |
11 |
1.6e+01 |
5 |
4504.5 |
71 |
65 |
6.4e+00 |
6 |
5323.5 |
265 |
270 |
-5.4e+00 |
7 |
6142.5 |
727 |
790 |
-6.3e+01 |
8 |
6961.5 |
1594 |
1610 |
-1.6e+01 |
9 |
7780.5 |
2317 |
2293 |
2.4e+01 |
10 |
8599.5 |
2390 |
2279 |
l.le+02 |
11 |
9418.5 |
1512 |
1582 |
-7.0e+01 |
12 |
10237.5 |
750 |
767 |
-1.7e+01 |
13 |
11056.5 |
232 |
260 |
-2.8e+01 |
14 |
11875.5 |
75 |
61 |
1.4e+01 |
15 |
12694.5 |
25 |
10 |
1.5e+01 |
16 |
13513.5 |
5 |
1 |
3.8e+00 |
17 |
14332.5 |
0 |
0 |
-9.4e-02 |
18 |
15151.5 |
0 |
0 |
-5.3e-03 |
19 |
15970.5 |
0 |
0 |
-2.le-04 |
ско |
= 7.715622 |
|
|
|
Обратите внимание на выравнивание значений в столбцах выводимой таблицы. Для этого выравнивания в обращении к функции printf() аккуратно подобраны спецификации преобра зования числовых значений (см. сошраге()). Например, пробел в спецификации "% 12.1е" обеспечивает пустую позицию на. месте знака положительного числа.
396 |
|
|
Программирование на языке Си |
|
char |
depname[15] ; |
/* Название отдела |
*/ |
|
int |
depnumb; |
/* Номер отдела |
*/ |
|
int |
price; |
/* |
Оклад |
*/ |
int |
job; |
/* |
Код должности |
*/ |
char |
jobname[15] ; |
/* |
Название должности |
*/ |
char |
date[10];/* Дата поступления на работу |
*/ |
};
Напомним, что приведенное описание не определяет струк туру с именем person. Вводится только "формат" входящих в структуру данных, и этот формат как структурный тип обозна чается именем person.
В нашей задаче запись о сотруднике учреждения (эта запись оформляется в виде конкретной структуры) должна быть эле ментом массива, содержащего сведения о всех сотрудниках. Массив структур определяется следующим образом:
struct person st[100];
Это определение отводит память для 100 индексированных переменных с общим именем st, каждая из которых является структурой типа person. Заметим, что, как и все массивы, масси вы структур индексируются, начиная с 0.
Если необходимо вывести на экран дисплея значение зара ботной платы сотрудника, идущего в списке под номером 4, это можно сделать следующим образом:
printf("%d", st[3].price);
Как и другие объекты программ на языке Си, структуры имеют адрес - номер байта, начиная с которого структура раз мещается в основной памяти. Со структурой можно связать ука затель, объявив его стандартным образом. Например:
struct person *point;
где point - указатель на структуру типа person.
Описав указатель на структуру, можно присвоить ему адрес конкретной структуры того же типа: point = &st[3];. После тако го определения значения указателя появляется еще одна воз
Глава 8. Примеры разработки программ |
397 |
можность доступа к элементам структуры. Ее обеспечивает опе рация позволяющая обратиться к любому элементу струк туры, с которой в данный момент связан указатель. Например, к тому же элементу st[3].price можно теперь обратиться, исполь зуя конструкцию point->price.
Напомним возможность применения указателя в уточненном имени элемента структуры. Операция раскрытия ссылки позво ляет обратиться к объекту, который адресует указатель, в част ном случае - на структуру. Таким образом, выражение (*point).price (обратите внимание на необходимость скобок, так как операция раскрытия ссылки относится только к указа телю point и ее приоритет ниже, чем у операции 7) эквива лентно point->price и st[3].price.
После того как определен состав сведений об одном сотруд нике и эта информация описана в виде структурного типа, необ ходимо определить способ хранения структур. Одним из вари антов хранения совокупности однотипных структур является массив фиксированного размера. Однако такая форма реализа ции базы данных не всегда удобна. Во время работы с массива ми, в которых записи размещены последовательно и, как правило, упорядочены по некоторому признаку, возникают про блемы, связанные с реорганизацией таких массивов при добав лении или удалении записей. Реорганизации массива можно избежать, если хранить записи базы данных в связных списках.
Техника работы со связными списками предполагает в слу чае ввода новой записи запрашивать для нее у операционной системы дополнительный объем памяти, а при удалении записи - возвращать соответствующий участок памяти операционной системе.
При программировании на языке Си для этой цели можно (как мы демонстрировали выше) использовать соответственно функции malloc(), calloc( ) и free(), но в рассматриваемом слу чае, учитывая неболыйой объем базы данных и учебный харак тер программы, будем использовать связный список с фиксированным количеством (100) элементов, формируя его из заранее выделенного массива. Чтобы записи с информацией о
398 |
Программирование на языке Си |
сотрудниках можно было связать в список, необходимо в струк туру, которая описывает сведения об одном сотруднике, ввести указатель на следующий элемент списка. Довольно часто ис пользуют двунаправленные списки, в которых каждый элемент списка содержит указатели как на предыдущий, так и на после дующий элемент. Схема двунаправленного связного списка, со стоящего из трех элементов, приведена на рис. 8.3.
Рис. 8.3. Пример двунаправленного связного списка из трех элементов в основной памяти
В примере предполагается использовать структурный тип person. Для того чтобы можно было связать отдельные струк туры в список, в структурный тип необходимо добавить два указателя:
•на предыдущий элемент списка (prior);
•на следующий элемент списка (next).
Полное описание структурного типа person будет иметь вид:
Глава 8. Примеры разработки программ |
399 |
|||
struct person |
|
|
|
|
{ |
name[20]; |
/* Ф . И. О. |
*/ |
|
char |
||||
char depname[15]; |
/* |
Название отдела |
*/ |
|
int |
depnumb; |
/* |
Номер отдела |
*/ |
int |
price; |
/* |
Оклад |
*/ |
int |
job; |
/* |
Код должности |
*/ |
char |
jobname[15]; |
/* |
Название должности |
*/ |
char date[10];/* Дата поступления на работу */
/* Указатель |
на предыдущий |
элемент: |
*/ |
struct person |
*prior; |
элемент: |
*/ |
/* Указатель |
на следующий |
||
struct person |
*next; |
|
|
};
Последние две строки (см. рис. 8.3) определяют указатели (prior и next) на структуры типа person, т.е. в качестве элементов структуры используются указатели на структуру того же типа. Так как список сотрудников располагается в основной памяти в массиве, содержащем фиксированное количество (100) экземп ляров структур типа person, а функции динамического распре деления памяти не используются, примем следующие техни ческие решения:
•при подготовке массива структур (т.е. при инициализации базы данных) все элементы массива объединяются в спи сок свободных элементов;
•список занятых элементов вначале пуст;
•при заполнении базы данных в качестве буферного исполь зуется первый из свободных элементов массива структур, в него записываются сведения об очередном сотруднике. За тем этот элемент включается в список занятых элементов. Таким образом, в массиве структур будут находиться два связных списка: список свободных элементов и список за нятых элементов.
Замечание. Вводимый список сотрудников должен быть упорядочен по алфавиту;
•при удалении сведений о сотруднике из базы данных осво бодившийся элемент удаляется из списка занятых элемен тов и возвращается в список свободных элементов.
400 |
Программирование на языке Си |
На рис. 8.4 изображен в рабочем состоянии массив структур, содержащий сведения о сотрудниках. Указатели в первых и последних элементах списков, не указывающие соответственно на предыдущий и следующий элементы, получают значение, равное NULL.
Начало |
|
Конец |
Начало |
|
Конец |
|
списка |
|
списка |
списка |
|
списка |
|
занятых |
|
занятых |
свободных |
свободных |
|
|
элементов |
|
элементов |
элементов |
элементов |
|
|
bbeg |
|
bend |
fbeg |
|
fend |
|
1 г |
Г |
' 1 , , 1 , |
1 |
Г ~ |
, J |
, |
INUL L I 11-++| Н |
Е М |
' |98|NELLI |
IN U L L I99|—М |
|IOO|N U L L | |
Список занятых элементов— ► <— Список свободных—► элементов
Рис. 8.4. Рабочее состояние массива структур (в списке свободных элементов сейчас только 2 элемента)
Схема, приведенная на рис. 8.5, поясняет процесс удаления элемента из списка занятых элементов. Был удален элемент под номером 2. Физически он остается на месте, но входит после
„удаления в другой список (свободных элементов), так как указа тели на предыдущий и последующий элементы получили дру гие значения.
Теперь можно приступить к разработке функций, реализую щих операции по обслуживанию базы данных. Для этого пона добятся следующие функции:
main() |
- главная функция; |
|
init() |
- |
инициализация (подготовка) базы данных; |
input() |
- |
ввод сведений о новом сотруднике; |
delete() - удаление сведений о сотруднике; |
||
print( ) |
- |
распечатка базы данных; |
save() |
- запись базы данных в файл; |
|
load() |
- загрузка базы данных из файла. |
Глава 8. Примеры разработки программ |
401 |
||
Начало |
Начало |
Конец |
Конец |
списка |
списка |
списка |
списка |
занятых |
свободных |
занятых |
свободных |
элементов |
элементов |
элементов |
элементов |
bbeg |
fbeg |
bend |
fend |
Рис. 8.5. Состояние массива структур после удаления элемента 2 из списка занятых элементов
Функция m ain(). В функции main() реализован механизм управления базой данных с помощью меню. Текст функции main() может быть таким (назначение отдельных переменных и частей программы поясняется в комментариях):
#include <stdio.h> |
|
|
|
|
|
|
*/ |
|
/* Определения глобальных объектов программы: |
||||||||
/* Опредение структурного типа "сотрудник": |
*/ |
|||||||
struct person |
|
|
|
|
|
|
|
|
{ |
|
/* Фамилия |
*/ |
|
|
|||
char name[20]; |
*/ |
|
||||||
char depname[15]; /* |
Название отдела |
|
||||||
int depnumb; |
/* |
Код |
отдела: |
*/ |
|
|||
|
|
|
/* |
1 |
- маркетинг |
*/ |
|
|
|
|
|
/* |
2 — реклама |
*/ |
|
||
int price; |
/* Оклад |
/* 3 - бухгалтерия */ |
|
|||||
*/ |
|
|
*/ |
|
||||
int job; /* |
Код должности: |
|
|
|||||
|
|
|
/* |
1 |
- менеджер |
*/ |
|
|
|
|
|
/* |
2 |
- |
агент |
*/ |
|
|
|
|
/* |
3 |
- |
эксперт |
*/ |
|
/* 4 - консультант */ char jobname[15]; /* Должность */
char date[10]; /* Дата поступления на работу: день.месяц.год */