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

книги / Программирование на языке Си

..pdf
Скачиваний:
15
Добавлен:
12.11.2023
Размер:
17.16 Mб
Скачать

392

Программирование на языке Си

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

И с х о д н ы е д а н н ые для п ро грам мы :

N - количество анализируемых псевдослучайных чисел; К - число интервалов гистограммы.

Для обмена с функциями нужно до заголовка main() опреде­ лить объекты внешней памяти.

Т ек с т

о с н о в н о й фу нкции

п ро г ра мм ы :

#include <stdio.h>

 

 

 

#include <stdl±b.h>

объектов:

*/

/*

Определение внешних

long N;

 

 

 

 

 

int К;

 

0;

 

 

 

 

int

smin =

 

 

 

 

int

smax =

16384;

 

 

 

int * gisto = NULL;

 

 

 

double

sum —

0.0;

 

 

 

double

sum2

=

0.0;

*/

 

 

/*

Прототипы

функций:

double

*, double *);

void estimate

(double

*,

'void compare(double, double); void count(void);

int main ( )

{int j ;

double mean, variance, mgist, vgist, dgist, cj ; printf("\n N=");

scanf ("%ld", £N);

if ( N <= 0)

{

printf("\n Ошибка в данных!"); return -1;

}

printf("\n K="); scanf("%d", &K); if ( К <= 0) •

Глава 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е" обеспечивает пустую позицию на. месте знака положительного числа.

Глава 8. Примеры разработки программ

395

8.2. С труктуры и обработка списков

в основной пам яти

Постановка задачи. Рассмотрим задачу, в которой весьма целесообразен структурный тип данных. Предположим, что в памяти ЭВМ необходимо хранить сведения о сотрудниках неко­ торого учреждения и иметь возможность выдавать справки по личному составу, а также корректировать сохраняемые данные при изменениях сведений. Таким образом, нужна несложная база данных о сотрудниках. Для каждого сотрудника должно быть указано (в скобках определен тип переменной):

фамилия (char[ ]);

название отдела (char[ ]);

номер отдела (int);

оклад (int);

код должности (int);

название должности (char[ ]);

дата поступления на работу (char[ ]).

Предположим также, что штатным расписанием установлена граница - не более 100‘ сотрудников, а при работе с этой базой данных потребуются следующие функции:

ввод сведений о новом сотруднике;

удаление сведений о сотруднике;

вывод на экран дисплея текущего состояния базы данных с ранжированием информации о сотрудниках по одному из следующих признаков:

-по окладам;

-по отделам;

-по алфавиту (по фамилиям);

-по датам поступления на работу.

Сведения о сотруднике для данной задачи будем определять с помощью структуры такого структурного типа:

struct person

{

c h a r n a m e s [ 2 0 ] ;

/ * Ф, И . О.

* /

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]; /* Дата поступления на работу: день.месяц.год */

Соседние файлы в папке книги