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

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

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

rz=pArray[i].mPoint.coord[2] – massCentr.mPoint.coord[2];

pArray[i].dist = sqrt(rx*rx + ry*ry + rz*rz); printf("Number: %d, coord={%f, %f, %f},\n"

"\t mass=%f, dist=%f\n", i, pArray[i].mPoint.coord[0], pArray[i].mPoint.coord[1], pArray[i].mPoint.coord[2], pArray[i].mPoint.mass, pArray[i].dist);

}

free (pArray);

}

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

Вводите сведения о точках: nextPoint.mPoint.mass=2<ENTER> nextPoint.mPoint.coord[0]=1<ENTER> nextPoint.mPoint.coord[1]=1<ENTER> nextPoint.mPoint.coord[2]=1<ENTER> nextPoint.mPoint.mass=2<ENTER> nextPoint.mPoint.coord[0]=-1<ENTER> nextPoint.mPoint.coord[1]=-1<ENTER> nextPoint.mPoint.coord[2]=-1<ENTER> nextPoint.mPoint.mass=0<ENTER>

Результаты обработки: Центр масс набора точек:

mass=4.000000, coords={0.000000, 0.000000,0.000000}

Сведения о точках набора:

Number: 0, coord={1.000000, 1.000000, 1.000000}, mass=2.000000, dist=1.732051

Number: 1, coord={-1.000000, -1.000000, -1.000000}, mass=2.000000, dist=1.732051

В программе два структурных типа struct massPoint и struct setPoint. Второй из них в качестве элемента включает структуру первого типа. Переменная int count – счетчик точек в наборе.

Для представления центра масс используется структура struct setPoint massCenter, инициализированная нулевыми значения-

471

ми элементов. Каждая новая точка вводится как значение элементов структуры struct setPoint nextPoint.

После ввода сведений об очередной точке в структуре остается неопределенным элемент nextPoint.dist – расстояние до центра масс. Если введенное значение nextPoint.mPoint.mass не положительно, то цикл ввода закончен. В противном случае на размер структуры увеличивается участок памяти, адресуемый указателем struct setPoint *pArray. (Используется функция realloc().) Новому элементу динамического массива pArray[count-1] присваивается введенная структура nextPoint. Затем в соответствии с приведенными соотношениями увеличивается масса системы точек

massCenter.mPoin.mass (

mi ) и "накапливаются" суммы про-

изведений:

i i

i i

i i

 

x m ,

y m ,

z m . После выхода из цикла вы-

числяются координаты xc, yc, zc, и печатаются сведения о центре

масс (mc, xc, yc, zc).

Переменная count – количество точек в наборе. Вспомогательные переменные rx, ry, rz – разности координат xc–xi, yc–yi, zc–zi, необходимые для вычисления расстояния от i-й точки до центра масс. Остальное очевидно из текста программы и результатов ее исполнения.

11.3.Структуры и функции

Структура и указатель на структуру могут служить параметрами функции. Функция может возвращать в точку вызова значение структуры или ее адрес (значение указателя на структуру). Последнее не корректно, если структура локализирована в теле функции как объект автоматической памяти.

ЗАДАЧА 11-10. Определите структурный тип для представления арифметических выражений с одной бинарной операцией и вещественными операндами. Бинарную операцию представляйте элементом типа char со значениями '+', '-', '*', '/'. Определите следующие три функции: для ввода с клавиатуры выражения (структуры); для вычисления значения выражения; для вывода результата вычисления выражения. В основной программе вводите выражения и печатайте их значения. Окончание работы – ввод неверного знака операции или нулевое значения делителя.

472

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

struct term {

double value1; double value2; char operation;

};

Функцию для чтения с клавиатуры сведений о структуре можно написать по-разному. Основное различие состоит в способе передачи результатов из функции в точку вызова. Используем возврат значения структуры, т.е. напишем функцию, возвращающую значение структуры:

/* readTerm.c - функция для ввода арифметического выражения */

#include <float.h> #include <stdio.h> #include <math.h> #include <stdlib.h> struct term readTerm()

{

struct term exp; char r; printf("value1=");

scanf("%le",&exp.value1);

printf("value2=");

scanf("%le",&exp.value2); printf("operation (+, -, *, /): ");

getchar();/*"Преодолеваем" трудность чтения char!*/ scanf("%c",&r); /* Вводим знак операции. */

if (r != '+' && r != '-' && r != '*' && r != '/')

{puts("Invalid operation!"); exit(0);

}

if (r == '/' && fabs(exp.value2) <= FLT_MIN) { puts("Invalid divisor!");

exit(0);

}

exp.operation = r; return exp;

}

473

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

/* valueTerm.c - функция для вычисления значения выражения */

double valueTerm(struct term exp)

{

switch

(exp.operation) {

case

'+':

case

return exp.value1 + exp.value2;

'-':

case

return exp.value1 - exp.value2;

'*':

case

return exp.value1 * exp.value2;

'/':

return exp.value1 / exp.value2; defaul:

puts("Invalid operation!"); exit(0);

}

}

Функция печати результатов вычисления выражения должна в качестве исходных данных получать структуру и не должна ничего возвращать в точку вызова:

/* solutionPrint.c - функция для вывода выражения */ void solutionPrint(struct term exp)

{

printf("(%g) %c (%g) equals %g\n", exp.value1, exp.operation, exp.value2, valueTerm(exp));

}

Обратите внимание, что для вычисления выражения функция печати результатов solutionPrint() обращается к функции valueTerm().

474

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

/* 11_10.c - структура "арифметическое выражение" */ #include <stdio.h>

struct term { double value1; double value2; char operation;

};

#include <math.h> #include <float.h> #include "readTerm.c" #include "valueTerm.c"

#include "solutionPrint.c" int main()

{

struct term expression; while(1)

{ puts("\nInput expression:"); expression=readTerm(); puts("\nExpression value:"); solutionPrint(expression);

}

return 0;

}

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

Input expression: value1=33<ENTER> value2=-55<ENTER>

operation (+, -, *, /): -<ENTER>

Expression value:

(33) - (-55) equals 88 Input expression: value1=4<ENTER>

475

value2=20<ENTER>

operation (+, -, *, /): /<ENTER>

Expression value:

(4) / (20) equals 0.2

Input expression: value1=0<ENTER> value2=0<ENTER>

operation (+, -, *, /): /<ENTER> Invalid divisor!

В функции main() определена структура expression, которой в бесконечном цикле присваиваются результаты, получаемые функцией readTerm() из входного потока. Затем эта структура используется в качестве аргумента функции solutionPrint(). Остальное очевидно из текста программы и приведенных результатов ее выполнения.

ЭКСПЕРИМЕНТ. Чтобы лучше понять взаимодействие функций и правила, используемые компилятором при трансляции, поменяйте местами препроцессорные директивы включения текстов функций solutionPrint() и valueTerm().

Трансляция будет завершена с предупреждающими сообщениями. Результаты выполнения будут совершенно не верны (см. про-

грамму 11_10_1.с).

ЭКСПЕРИМЕНТ. В программе возвращаемое функцией readTerm() значение присваивается структуре expression, которая затем используется в качестве аргумента функции solutionPrint(). Удалите структуру expression, т.е. используйте вызов функции readTerm() в качестве аргумента функции solutionPrint().

Придется также удалить печать сообщения "Expression value:" – оно не к месту. См. программу 11_10_2.с.

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

476

В этом случае необходимо передавать в функцию адрес того объекта (той структуры типа struct term), которая будет изменяться при вводе данных. Прототип функции может быть таким (см. про-

грамму 11_10_3.с):

void readTerm1(struct term *);

Обращение к этой функции из функции main() придется заменить на такое:

readTerm1(&expression);

В этом случае структуру expression никак не удается исключить из цепочки передач данных от клавиатуры к функции solutionPrint().

ЭКСПЕРИМЕНТ. Структура как и массив относится к агрегирующим типам данных. Возникает желание проверить, нельзя ли изменять значения элементов структуры, используя ее имя в качестве параметра функции. Попытайтесь сделать это, введя функцию с таким прототипом: void readTerm2(struct term);.

Обращение из функции main сделаем таким (см. программу

11_10_4.с):

readTerm2(expression);

Что самое неприятное в такой ошибке – ее не выявляет ни компилятор, ни компоновщик (редактор связей). После выполнения функции readTerm2() ее аргумент (конкретная структура expression) остается без изменений и только анализ значений ее элементов позволяет выявить отсутствие передачи данных из readTerm2().

Функция readTerm() программы 11_10.с возвращает значение структуры. Покажем, какие особенности у функции, возвращающей адрес структуры (указатель на структуру).

477

ЗАДАЧА 11-11. Подсчитайте, сколько раз каждое служебное слово языка Си встречается во входном потоке. Используйте массив структур из программы 11_06.с. Определите и используйте функцию, анализирующую слово, представленное строкой. Функция должна возвращать адрес той структуры (того элемента массива структур), в которой слово присутствует как значение компонента.

Для чтения одной строки входного потока, как и в программе 11_06.с, применим функцию readLine() из темы 9. Прочитав входную строку, будем выбирать из нее от ее начала до конца последовательности строчных латинских букв от 'a' до 'z'. Каждая такая последовательность может оказаться служебным словом. Для проверки определим функцию с прототипом

struct keyword * analyse(char * letters,

struct keyword array[], int num);

Здесь struct keyword – структурный тип; array[] – массив структур этого типа; num – количество элементов массива; letters – строка, представляющая проверяемое слово (последовательность символов). Функция возвращает адрес элемента массива с проверяемым словом или NULL, если слово не является служебным.

Структурный тип struct keyword определим как глобальный. Для сравнения символьных строк используем библиотечную функцию

int strcmp(char * s1, char * s2);

Библиотечная функция

char * strpbrk(char * s1, char * s2);

позволяет найти в строке s2 адрес первого (слева) любого символа из строки s2;

Библиотечная функция

int strspn(char * s1, char * s2);

возвращает длину найденной в s1 последовательности символов, каждый из которых присутствует в s2.

478

Для "выбора" последовательности символов из строки используем библиотечную функцию копирования:

int strncpy(char * s1, char * s2, int len);

Из строки s2 в строку s1 копируются len символов. Решение задачи может быть таким:

/* 11_11.c – подсчет служебных слов (функция возвращает указатель на структуру) */

#include <string.h> #include <stdio.h> #include "readLine.c"

struct keyWord{ char * word; int counter;

};

struct keyWord * analyse

(char * letters, struct keyWord array[], int num)

{

 

 

 

int i;

 

 

 

for (i=0; i < num; i++)

letters) == 0)

if (strcmp(array[i].word,

return & array[i];

 

 

return NULL;

 

 

 

}

 

 

 

int main()

 

 

 

{

 

 

 

struct keyWord * pword, stWord[] =

"char",0,

{"auto",0,

"break",0,

"case",0,

"const",0,

"continue",0, "default",0, "do",0,

"double",0,

"else", 0,

"enum",0,

"extern",0,

"float",0,

"for",0,

"goto",0,

"if",0,

"int",0,

"long",0,

"register",0,

"return",0,

"short",0,

"signed",0,

 

"sizeof",0,

"static",0,

"typedef",0, "union",0,

"struct",0,

"switch",0,

"unsigned",0, "void",0,

"volatile",0, "while",0

};

#define AR_SIZE (sizeof(stWord)/sizeof(stWord[0])) int i, len;

char * string, temp[30];

479

puts("Input text (EOF - end of run):"); while((string = readLine()) != NULL)

{

while(1)

{ string = strpbrk(string, "abcdefghijklmnopqrstuvwxyz");

if (string == NULL) break; len = strspn(string,

"abcdefghijklmnopqrstuvwxyz"); strncpy(temp, string, len);

temp[len] ='\0'; string += len;

pword = analyse(temp, stWord, AR_SIZE); if (pword == NULL) continue;

pword -> counter++;

}

}

for (i=0; i<AR_SIZE; i++)

if (stWord[i].counter != 0) printf("\n%s - %d", stWord[i].word,

stWord[i].counter);

return 0;

}

Командная строка запуска программы для анализа текста из фай-

ла 11_10.с:

C:\...>test <11_10.c<ENTER>

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

Input text (EOF - end of run): char - 1

double - 2 float - 1 int - 1 return - 1 struct - 2 while - 1

В функции analyse()

адресованная

параметром char

* letters строка с набором

латинских букв

(последовательность

480