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

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

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

Для динамического формирования структуры потребуется определить ее структурный тип struct triangle и соответствующий указатель struct triangle * ptri. Для ввода длин сторон треугольника удобен уже использованный нами макрос READD(). Для вывода результатов – макрос PRINTF(). Для оценки свойств треугольника определим вспомогательные функции:

// triAnalysis.c - функция для анализа треугольников void rank2(double * x, double * y)

{

double

m;

if (*x

> *y)

{ m=*x;

*x=*y; *y=m; }

}

/* Упорядочить значения трех переменных */

void sort(double * min, double * mid, double * max)

{

rank2(min, mid); rank2(mid, max); rank2(min, mid);

}

/* a, b, c - стороны треугольника */

char * triAnalysis(double a, double b, double c)

{

if (a+b<=c||a+c<=b||b+c<=a)

"не треугольник";

 

return

if (a == b && b == c) return

"равносторонний";

if (a == b || b == c || a ==

c)

sort(&a, &b, &c);

return

"равнобедренный";

return "прямоугольный";

if (c*c == a*a + b*b)

if (c*c > a*a + b*b)

return "тупоугольный";

if (c*c < a*a + b*b)

return "остроугольный";

return "ошибка!";

 

 

}

Все три функции разместим в одном текстовом файле triAnalysis.c. Основной является функция triAnalys(), получающая три вещественных значения – потенциальные длины сторон треугольника. Если значения аргументов не удовлетворяют требова-

461

ниям к длинам сторон треугольника, функция возвращает сообщение "не треугольник". В противном случае определяется тип треугольника. Если он не равносторонний и не равнобедренный, то с помощью функции sort() длины сторон упорядочиваются и квадрат максимальной сравнивается с суммой квадратов двух других. Остальное очевидно из текста функции. Обратите внимание, что функция sort() использует дополнительную функцию rank(), "переставляющую" значения двух переменных, адресованных ее аргументами.

При выводе (на экран MS-DOS-приложений) русского текста потребуется перекодировка. Ее будем выполнять с помощью функции rusDOS(), определенной в теме 9.

С учетом сказанного, решение задачи может быть таким:

/* 11_07.c - структурный тип "треугольник" */ #include <stdio.h>

#include <string.h> #include <math.h> #include <stdlib.h> #include "rusDOS.c" #include "triAnalysis.c" #define READD(VARIABLE) \

{printf(#VARIABLE"="); scanf("%le",&VARIABLE);} #define PRINTF(EXPRESSION) \

printf(#EXPRESSION"=%f\n",EXPRESSION) int main()

{

struct

triangle { /* структура "треугольник" */

double

x ,y, z; /* длины сторон */

double

sqr;

/*

площадь треугольника */

char

name[40];

/*

название типа треугольника */

};

struct triangle * ptri; double a, b, c, p; READD(a);

READD(b);

READD(c);

if (a+b<=c||a+c<=b||b+c<=a)

{printf("\nError!"); return 1;

}

ptri = (struct triangle *)

462

malloc(sizeof(struct triangle)); if (ptri == NULL)

{puts("The malloc() error!"); exit(0);

}

a;

ptri -> x =

ptri -> y =

b;

ptri -> z =

c;

p = (a+b+c)/2;

ptri -> sqr

= sqrt(p*(p-a)*(p-b)*(p-c));

strcpy(ptri

-> name, triAnalysis(a,b,c));

puts(rusDOS("Сведения о треугольнике:")); PRINTF(ptri -> x);

PRINTF(ptri -> y); PRINTF(ptri -> z); PRINTF(ptri -> sqr);

printf("%s", rusDOS("Треугольник: ")); printf("%s", rusDOS(ptri -> name)); return 0;

}

Результаты выполнения программы.

Первое исполнение:

a=3<ENTER>

b=4<ENTER>

c=5<ENTER>

Сведения о треугольнике: ptri -> x=3.000000

ptri -> y=4.000000 ptri -> z=5.000000 ptri -> sqr=6.000000

Треугольник: прямоугольный

Второе исполнение:

a=5<ENTER>

b=6<ENTER>

c=27<ENTER>

Error!

463

В тексте программы обратите внимание на необходимость приведения типов при выделении памяти для структуры. Дело в том, что функция malloc() всегда возвращает указатель типа void *, а указатель ptri на структуру имеет тип struct triangle *. Для доступа по указателю к элементам структуры используется операция ->. Остальное очевидно.

ЗАДАНИЕ. Замените в тексте программы операцию -> двумя операциями: разыменования указателя (*) и прямой выбор элемента (операция "точка").

В программе 11_07_1.с задание выполнено.

ЗАДАЧА 11-08. Подсчитайте, сколько раз во входном потоке встречается каждый изображаемый символ. (Упражнение 1.14 из классического пособия [1].)

Введем структурный тип struct symbol с элементами char symb – для представления символа и int frequency – для подсчета количества использования символа во входном потоке. Так как общее количество различных символов заранее не известно, то возможны два решения – создание массива структур с фиксированным достаточно большим количеством элементов и использование динамического растущего массива структур.

Будем использовать растущий динамический массив структур, адресуя его указателем struct symbol * psymbol. В структуре struct symbol определим два компонента: cahr symb – для символа; int frequency – для счетчика появлений символа во входном потоке. Цикл чтения символов входного потока ограничен появлением кода EOF. Чтобы выделить из читаемых символов только изображаемые (печатаемые), используем описанную в заголовке ctype.h библиотечную функцию

int isprint(int c);

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

464

if (isprint (c) && c!=' ')

Для каждого символа "c", удовлетворяющего этому условию, необходимо:

Проверить присутствие "с" в массиве структур. Если "с" найден в массиве:

Увеличить счетчик этого символа (компонент frequency).

Впротивном случае:

Добавить в конец массива элемент (структуру struct symbol). Присвоить компоненту symb этой структуры значение "с". Присвоить компоненту frequency значение 1.

Для подсчета числа элементов в структуре введем переменную int len. Для увеличения массива используем функцию realloc().

/*11_08.c - растущий динамический массив структур */ #include <stdio.h>

#include <ctype.h> #include <stdlib.h>

struct symbol {/*структурный тип "частота символа"*/

char symb;

/*

символ */

int frequency;

/*

количество появлений */

};

 

 

int main()

{

int c, i=0, len=0;

struct symbol * psymbol=NULL; puts("Input text (^Z - end of run!): "); while ((c=getchar()) != EOF)

{if (isprint(c) && c != ' ')

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

if (psymbol[i].symb == c)

{psymbol[i].frequency++;

break;

}

if (i >= len)

{ psymbol=(struct symbol *)realloc(psymbol, (++len)*sizeof(struct symbol));

if (psymbol == NULL)

{puts("The relloc() error!"); exit(0);

465

}

psymbol[len-1].frequency = 1; psymbol[len-1].symb = c;

}

}

}

puts("Results: "); printf("len=%d",len); for (i=0; i < len; i++)

{ printf("%c%c(%i) - %d", i%3 == 0 ? '\n':'\t', psymbol[i].symb, psymbol[i].symb, psymbol[i].frequency);

}

free (psymbol);

}

Запуск программы на выполнение (обработаем текст программы

11_07.с):

C:\...>test<11_07.c

Результаты выполнения программы:

Input text (^Z - end of run!): Results:

len=72

/(47) - 13 *(42) - 17 1(49) - 3 _(95) - 5 0(48) - 28 7(55) - 2

.(46) - 12 c(99) - 34 -(45) – 18

...............

Практически все переменные программы упомянуты выше. Обратите внимание, что указатель psymbol инициализирован значением NULL. При прочтении из входного потока очередного символа код символа проверяется функцией isprint() и сравнивается с пробелом. Если символ допустим, но отсутствует в структурах массива, выполняется функция realloc(). Участок памяти, адресуемый psymbol, увеличивается на значение sizeof(struct symbol), и увеличивается на 1 значение переменной len.

При выполнении программы в нашем примере обрабатывался текст программы 11_07.с. Интересен и важен тот факт, что в ре-

466

зультатах отсутствуют русские буквы – функция isprint() "понимает" только символы из первой половины кодовой таблицы с кодами от 32 до 127.

ЗАДАНИЕ. Измените проверку допустимости прочитанных символов, чтобы анализировались все символы кодовой таблицы (обязательно включались русские буквы).

Вместо обращения к библиотечной функции isprint() введем условие:

if(c < 256 && c > 32)...

В определении структурного типа сменим char symb; на unsigned char symb;. Результаты можно просмотреть в

11_08_1.с.

ЭКСПЕРИМЕНТ. Не изменяйте тип элемента структуры char symb. Оттранслируйте и выполните этот вариант программы 11_08_1.с. Введите несколько одинаковых русских букв и объясните результат.

ЗАДАЧА 11-09. Определите структурный тип "точка в наборе материальных точек". Элементы: структура "материальная точка" (из программы 11_03.с) и ее расстояние до центра тяжести набора материальных точек. В программе вводите массы и координаты точек. Определите их центр масс, затем вычислите расстояние от найденного центра до всех точек набора. Окончание ввода – нулевое значение массы точки. Напечатайте сведения о точках в созданном наборе.

Как уже говорилось в связи с программой 11_03.с, материальная точка может быть представлена следующей четверкой: {m, x, y, z}, где m – масса точки; x, y, z – ее координаты. Общая масса

(сосредоточенная в центре масс) системы точек: mi = mi . Коор-

динаты центра масс: xc =

mi xi

,

yc =

mi yi

, zc =

mi zi

,

 

mc

 

mc

 

mc

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

467

где xi, yi, zi – координаты отдельной точки, mi – ее масса. Ее расстоя-

ние от центра масс:

(x

c

x )2+

( y

y )+2

(z

c

z )2 .

 

 

i

c

i

 

i

Точку в наборе точек будем представлять как материальную точку плюс ее расстояние от центра масс. Центр масс можно представить как особую точку набора с нулевым расстоянием от центра.

Введем структурный тип "точка из набора" (struct setPoint), определим "центр масс" massCenter как структуру этого типа. По мере ввода сведений о точках будем формировать растущий динамический массив структур "точка из набора" и одновременно определять (уточнять) центр масс. Закончив ввод, определим расстояние от каждой точки до центра масс и выведем сведения о каждой точке.

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

double [3] – пространственные координаты;

struct massPoint – представление материальной точки; struct setPoint – представление точки из набора; struct setPoint[] – совокупность точек набора (массив).

Обозначим элемент типа struct massPoint, входящий в структуру типа struct setPoint, именем mPoint.

Введем указатель на растущий динамический массив: struct

setPoint

*pArray. Тогда соотношение между объектами агреги-

рующих типов можно изобразить следующей схемой:

 

Массив

Структура

 

Структура

Переменная

 

(элемент

 

(элемент

(элемент

 

 

массива)

структуры)

массива)

pArray[i]

 

mPoint

 

 

coord[3]

 

coord[0]

 

 

 

 

 

 

 

 

 

 

 

coord[1]

 

 

 

 

 

 

 

coord[2]

Учитывая эту схему, несложно (но все же громоздко) записывать уточненные имена элементов структур. Например, вторая (с индексом 1) координата точки, служащей третьим (с индексом 2) элементом динамического массива структур, именуется так:

pArray[2].mPoint.coord[1]

468

Напомним, что pArray[2] соответствует *(pArray+2), таким образом можно было бы записать эквивалентное выражение:

(pArray+2)–>mPoint.coord[1]

Общая схема алгоритма решения задачи будет такой:

Цикл до конца ввода Ввести сведения о точке

Увеличить массив для точек набора Занести сведения о точке в новый элемент массива

Изменить сведения о центре масс набора точек Конец-цикла Цикл по элементам массива точек

Вычислить расстояние от точки до центра Напечатать сведения о точке

Конец-цикла

Текст программной реализации алгоритма:

/*11_09.c - массив структур "Набор материальных точек" */

#include <stdio.h> #include <math.h> #include <stdlib.h> #include "rusDOS.c"

#define

READD(VARIABLE) \

{printf(#VARIABLE"="); scanf("%le",&VARIABLE);}

struct massPoint {

/* материальная точка */

double

coord[3];

/* координаты точки */

double

mass;

/* масса точки */

};

 

/* точка в наборе точек */

struct setPoint {

struct

massPoint

mPoint;/* материальная точка */

double

dist;

/* расстояние от центра масс */

};

 

 

int main()

 

{

count = 0;

 

int i,

 

double

rx, ry, rz;

struct

setPoint massCentr = {0., 0., 0., 0., 0. };

 

 

469

struct setPoint nextPoint; struct setPoint * pArray = NULL;

puts(rusDOS("Вводите сведения о точках:")); while(1)

{READD(nextPoint.mPoint.mass);

if (nextPoint.mPoint.mass <= 0) break; READD(nextPoint.mPoint.coord[0]); READD(nextPoint.mPoint.coord[1]); READD(nextPoint.mPoint.coord[2]); count++;

pArray=(struct setPoint *) realloc(pArray,count*sizeof(struct setPoint));

pArray[count-1] = nextPoint; massCentr.mPoint.mass += nextPoint.mPoint.mass; massCentr.mPoint.coord[0] +=

nextPoint.mPoint.coord[0]

*nextPoint.mPoint.mass; massCentr.mPoint.coord[1] +=

nextPoint.mPoint.coord[1]

*nextPoint.mPoint.mass; massCentr.mPoint.coord[2] +=

nextPoint.mPoint.coord[2]

*nextPoint.mPoint.mass;

}

massCentr.mPoint.coord[0]/=massCentr.mPoint.mass;

massCentr.mPoint.coord[1]/=massCentr.mPoint.mass;

massCentr.mPoint.coord[2]/=massCentr.mPoint.mass; puts(rusDOS("Результаты обработки: ")); puts(rusDOS("Центр масс набора точек: ")); printf("mass = %f, coords = {%f, %f,%f}\n",

massCentr.mPoint.mass,

massCentr.mPoint.coord[0],

massCentr.mPoint.coord[1],

massCentr.mPoint.coord[2]); puts(rusDOS("Сведения о точках набора: ")); for(i=0; i < count; i++)

{

rx=pArray[i].mPoint.coord[0] – massCentr.mPoint.coord[0];

ry=pArray[i].mPoint.coord[1] – massCentr.mPoint.coord[1];

470