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

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

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

вое, например нулевое, значение всем элементам структуры, придется выписывать уточненные имена всех ее элементов:

result.mass=0.0;

result.coord[0]=0.0;

и т.д.

То же самое применение уточненных имен требуется при вводе или выводе значений элементов структуры.

ЗАДАЧА 11-04. Определите структурный тип для представления значений вещественной функции совместно со значениями n-мерного вектора ее аргументов. Определите структуру этого типа, введите значения ее элементов и напечатайте их.

Каждому набору из n значений аргументов должно быть поставлено в соответствие одно значение функции. Пусть значения аргументов и значения функций являются вещественными типа double. Тогда элементы структуры могут быть такими:

int dim – размерность пространства аргументов (значение n); double coords[dim] – значения аргументов функции;

double value – значение функции.

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

/* 11_04.c

– ресурсоемкая cтруктура "значение

#include

 

многомерной функции" */

<stdio.h>

 

#include

<stdlib.h>

struct nFun {/*многомерная функция*/

int dim;

/*размерность пространства аргументов*/

double

*coords;

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

double

value;

/* значение функции */

};

#define PRINTZ(TERM) \ printf("sizeof("#TERM")=%d\n",sizeof(TERM))

451

int main()

{

struct nFun myFun; int i;

printf("Input dimension: "); scanf("%d",&myFun.dim); myFun.coords =

(double *)calloc(myFun.dim, sizeof(double)); if (myFun.coords == NULL)

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

}

for (i=0; i < myFun.dim; i++)

{printf("coords[%d] : ",i); scanf("%lf",&myFun.coords[i]);

}

printf("function value: "); scanf("%lf",&myFun.value); printf("\nmyFun:\n"); printf("dimension = %d",myFun.dim); printf("\nArgument array:");

for (i=0; i<myFun.dim; i++) printf("%c[%d]=%5.2f",

i%4?'\t':'\n',i,myFun.coords[i]); printf("\nfunction value: %e\n",myFun.value); PRINTZ(myFun);

free(myFun.coords); return 0;

}

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

Input dimension: 3<ENTER> coords[0] : 5.67<ENTER> coords[1] : -94.6<ENTER> coords[2] : 44.89<ENTER> function value: 89021<ENTER>

myFun: dimension = 3 Argument array:

452

[0]= 5.67 [1]=-94.60 [2]=44.89 function value: 8.902100e+04 sizeof(myFun)=16

В программе вне ее функций определен структурный тип struct nFun. Динамический массив значений аргументов функции адресован в каждой структуре этого типа указателем double * coords; В программе определена конкретная структура myFun. Вводится из входного потока размерность пространства аргументов myFun.dim. Затем библиотечной функцией calloc() выделяется память для массива аргументов, и в цикле вводятся их значения. Завершает определение структуры ввод значения myFun.value. После этого печатаются не только элементы структуры, но и массив, который адресован ее элементом – указателем myFun.coords. В завершение с помощью макроса PRINTZ() выводится размер структуры (16 байт

=sizeof(int)+sizeof(double *)+sizeof(double)). Как ви-

дите, в нем не учтено существование динамического массива, адресованного элементом coords.

ЗАДАНИЕ. Дополните программу 11_04.с, введя в нее еще одну структуру "значение многомерной функции". Присвойте ей значение структуры myFun. Затем "обнулите" все элементы структуры myFun, включая массив аргументов функции, и выведите значения всех элементов новой структуры.

Условие полностью эквивалентно условию из задания к задаче 11_03.с. Но внутреннее "устройство" структур "материальная точка" и "значение многомерной функции" принципиально различны. В последнем случае массив не входит в структуру и при присваивании структур остается недоступным. Определим в программе (см. файл 11_04_1.с) структуру и выполним присваивание:

struct nFun testFun;

...

testFun=myFun;

Затем всем элементам структуры myFun присвоим нулевые значения и выведем значения элементов testFun. Получим при выполнении программы, например, такой результат:

453

Input dimension: 3

 

coords[0] : 3.6

 

 

coords[1] : 43.0

 

 

coords[2] : 12.9

46.3

 

function value:

 

myFun:

 

 

dimension = 3

 

 

Argument array:

[1]=43.00

[2]=12.90

[0]= 3.60

function value:

4.630000e+01

 

sizeof(myFun)=16

 

 

testFun:

 

 

dimension = 3

 

 

Argument array:

[1]= 0.00

[2]= 0.00

[0]= 0.00

function value:

4.630000e+01

 

Хотя присваивание testFun=myFun выполнено до обнуления структуры myFun, но динамический массив у структуры textFun обнулен! Две структуры накрепко "связаны" тем, что их элементы адресуют один и тот же участок динамически выделенной памяти.

ЗАДАЧА 11-05. Определите структурный тип "город" с элементами "название" и "число жителей". Определите структурный тип "государство" с элементами "название страны", "столица", "площадь территории", "численность населения". В структуре "государство" сведения о столице представляйте с помощью структуры "город". В основной программе определите структуру "государство". Инициализируйте ее сведениями о Бразилии до 1960 г.: Brazil (название страны), Rio de Janeiro (столица), 8500000 (площадь страны, км2), 109000000 (население страны), 4900000 (число жителей столицы). Введите с клавиатуры сведения о новой столице (с 1960 г.): Brasilia (название города), 272000 (число его жителей). Напечатайте все сведения о Бразилии с новой столицей.

/*11_05.c - структуры "город" и "страна" */ #include <stdio.h>

454

struct city {

/*структурный тип "город" */

char name[25];

/*название города */

int people;

/*число жителей */

};

/*структурный тип "страна" */

struct country {

char name[25];

/*название государства */

struct city capital; /*столица */

long volume;

/*численность населения страны*/

int area;

/* площадь территории */

};

 

int main()

 

{

 

struct country state = {"Brazil",

"Rio de Janeiro", 4900000, 109000000, 8500000}; printf("New Capital of %s: \nName: ",state.name); scanf("%s",state.capital.name);

printf("Capital people number: "); scanf("%d",&state.capital.people); printf("\nCountry: %s",state.name); printf("\nCapital: %s",state.capital.name); printf("\nCapital people quantity: %d",

state.capital.people);

printf

("\nCountry people number: %ld",state.volume); printf("\nCountry area: %d km*km",state.area); return 0;

}

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

New Capital of Brazil:

Name: Brasilia<ENTER>

Capital people number: 272000<ENTER>

Country: Brazil

Capital: Brasilia

Quantity: 272000

Country people number: 109000000

Country area: 8500000 km*km

455

ЗАДАНИЕ. Используя структурные типы struct city и struct country из рассмотренной программы, определите отдельно структуру Spain (Испания) и отдельно структуру Madrid (Мадрид). Элементы структуры Madrid определите при инициализации (название "Madrid", число жителей: 3.6 млн. чел.). Присвойте соответствующему элементу структуры Spain значение структуры Madrid, значения остальных элементов введите с клавиатуры (площадь: 503 км2, население: 36 млн.чел.). Напечатайте сведения об Испании.

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

Spain.capital=Madrid;

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

11_05_1.с.

ЗАДАЧА 11-06. Подсчитайте, какие служебные слова и сколько раз встречаются в тексте программы на языке Си. Выведите информацию о тех служебных словах, которые использованы в тексте.

Взадаче удобно использовать массив структур stWord[], каждая из которых включает служебное слово и счетчик его появлений в тексте. Анализируемый текст будем считывать из стандартного входного потока, перенастраивая (переназначая) его на нужный файл. Алгоритм решения задачи может быть таким:

Вцикле до конца входного потока:

Прочесть очередную строку Цикл перебора всех служебных слов

Если в строке найдено служебное слово Увеличить счетчик этого слова В строке заменить слово пробелом

Подготовить повторный поиск слова в строке Все-если

Конец-цикла Конец-цикла

Для чтения из входного потока очередной строки используем функцию readLine() из темы 9. Функция формирует в динамиче-

456

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

char * strstr(char * str, char * w);

Если последовательность символов слова, адресованного параметром w, найдена в строке, адресованной параметром str, функция strstr() возвращает адрес ее начала в строке str. В противном случае результат NULL. Если последовательность символов служебного слова найдена в строке, то необходимо проанализировать, является ли эта последовательность служебным словом. Например, в строке "printf()" присутствует последовательность "int", но она не представляет служебного слова int.

Будем считать, что последовательность символов представляет служебное слово, если выполняются следующие два условия. Перед ним находится один из символов: пробел, '\', запятая, '\n', '(', '{' либо слово находится в начале строки. Второе условие – слово ограничено слева одним из символов: пробел, запятая, '\t', '\n', '{', ')'. Для проверки предшествующего и последующего символов используем библиотечную функцию

char * strchr(char * str, char symb);

Если в строке, адресуемой указателем str, отсутствует символ symb, то strchr() возвращает NULL.

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

void * memcpy(void * p, void * s, int len);

Функция заменяет len символов в объекте, адресованном указателем p, на len символов из объекта, адресуемого указателем s.

457

Изменив таким образом анализируемую строку, повторим в ней поиск того же слова. Для этого достаточно в теле цикла уменьшить на единицу параметр i цикла, служащий индексом при переборе элементов массива структур со словами.

Описанный алгоритм реализует следующая программа:

/* 11_06.c - служебные слова в массиве структур */ #include <string.h>

#include <stdio.h> #include "readLine.c" int main()

{

struct { char * word; int counter;

}stWord[] ={"auto",0, "break",0,

"case",0,

"char",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,

"struct",0,

"switch",0,

"typedef",0,

"union",0,

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

"volatile",0,

"while",0 };

 

 

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

char * string, * pstr;

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

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

if ((pstr=strstr(string,stWord[i].word))!=NULL)

{len = strlen(stWord[i].word); if (pstr != string &&

strchr("(; \t,\n}",*(pstr-1))==NULL) continue; /* слева недопустимый символ */

if (strchr(") \t\n({\0,",*(pstr+len))==NULL)

continue; /* справа недопустимый символ */ stWord[i].counter++;

memcpy(pstr," ",len); i--;

}

}

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

458

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

stWord[i].word,stWord[i].counter);

free(string); return 0;

}

При стандартном назначении входного потока stdin (при вводе с клавиатуры) признак окончания файла EOF нужно имитировать, вводя сочетание клавиш "Ctrl+Z".

Результаты выполнения программы (при вводе с клавиатуры):

Input text (EOF - end of run): if then do<ENTER>

^z<ENTER> do – 1

if – 1

Переназначим входной поток на чтение из текстового файла

11_01.с:

Командная строка запуска программы:

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

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

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

int - 2 long - 1 return - 1 sizeof - 7 struct - 4

В тексте программы массив структур stWord[] определяется и инициализируется без именования соответствующего структурного типа. Каждый элемент stWord[i] массива является структурой, которая содержит указатель char * word на строку (служебное слово) и счетчик (int counter). Все счетчики при инициализации получают нулевые значения. Препроцессорный идентификатор AR_SIZE пред-

459

ставляет выражение, вычисляющее количество элементов в массиве структур. Указатель char * string адресует очередную прочитанную из входного потока строку. Указатель char * pstr получает адрес начала последовательности символов служебного слова в строке, адресованной string. Переменной int len присваивается значение длины анализируемого слова. Поэтому *(pstr-1) – символ, предшествующий символам слова, *(pstr+len) – символ, находящийся после них. Если последовательность найдена в строке, но справа или слева от нее находится недопустимый символ, то оператор continue "переводит" внутренний цикл к следующей итерации.

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

В результатах показан анализ текста программы 11_01.с. Сравните полученные значения с текстом программы 11_01.с. Например, обратите внимание, что слово struct обнаружено 4 раза, причем один раз в комментариях. Слово sizeof обнаружено 7 раз. Но в тексте программы 11_01.с оно встречается 9 раз, причем является служебным (согласно синтаксису языка Си) только 1 раз. В остальных случаях оно находится в комментарии (в результатах) и один раз в строковой константе. Таким образом, наша программа – весьма несовершенный распознаватель служебных слов и требует доработок, которые мы оставляем читателю для самостоятельного выполнения.

11.2.Указатели на структуры

ЗАДАЧА 11-07. Введите длины трех отрезков (x, y, z). Если отрезки могут служить сторонами треугольника, то создайте структуру, включающую: длины сторон треугольника (x, y, z), его площадь (s) и название типа треугольника: "тупоугольный", "прямоугольный", "остроугольный", "равносторонний", "равнобедренный".

Напомним, что величины x, y, z могут быть длинами сторон треугольника, если сумма любых двух из них больше третьей. Площадь можно определить по формуле Герона:

s =

p( p x)( p

y)( p z), где p =

x + y + z

.

2

 

 

 

 

460