Разрешенными операциями над структурами являются копирование или присваивание структуры как целого, взятие ее адреса операцией , а также обращение к ее элементам [15.1]. Копирование и присваивание включает в себя также передачу аргументов в функции и возвращение значений из функций.
Возможна передача членов структур функциям и передача целых структур функциям. При передаче функции члена структуры передается его значение, притом не играет роли то, что значение берется из члена структуры. Например [15.2], пусть задана структура следующего вида:
struct fred
{
char x;
int y;
float z;
char str[10]; // с учетом символа окончания строки
} mike;
Тогда каждый член этой структуры можно передать функции, например,
func(mike.x); // передается символьное значение х func2(mike.y);// передается целое значение y func3(mike.z);// передается значение с плавающей точкой z func4(mike.str);// передается адрес строки str[10] func(mike.str[2]);// передается символьное значение str[2]
Если же нужно передать адрес отдельного члена структуры, то перед именем структуры должен находиться оператор . Для рассмотренных примеров будем иметь
func(mike.x); // передается адрес символа х func2(mike.y);// передается адрес целого y func3(mike.z);// передается адрес члена z с плавающей точкой func4(mike.str);// передается адрес строки str func(mike.str[2]);// передается адрес символа в str[2]
Когда в качестве аргумента функции используется структура, для передачи целой структуры используется обычный способ вызова по значению [15.2]. Это означает, что любые изменения в содержимом параметра внутри функции не отразятся на той структуре, которая передана в качестве аргумента.
При использовании структуры в качестве параметра надо помнить, что тип аргумента должен соответствовать типу параметра. В качестве примера рассмотрим следующий программный код [15.2]:
#include <stdio.h>
#include <conio.h>
// Определение глобального типа структуры
struct struct_type {
int a, b;
char ch;
};
// Прототип функции
void fun(struct struct_type parm);
int main (void) {
struct struct_type arg;
arg.a = 1000;
fun(arg);
printf("\n Press any key: ");
_getch();
return 0;
}
// Определение функции
void fun(struct struct_type parm)
{
printf("\n %d\n", parm.a);
}
Назначение этой программы состоит в печати заданных полей структуры, которая объявлена как глобальная, чтобы структурный тип был виден во всей программе.
Когда используется указатель на структуру, вместо оператора точки используется оператор "стрелка". Видоизменим рассмотренную программу, введем в нее указатель на структуру:
#include <stdio.h>
#include <conio.h>
// Определение глобального типа структуры
struct struct_type { int a, b; char ch; };
// Прототип функции
void fun(struct struct_type *parm);
int main (void)
{
struct struct_type arg,*PTR;
PTR = arg;
PTR->a = 999;
fun(PTR);
printf("\n Press any key: ");
_getch();
return 0;
}
// Определение функции
void fun(struct struct_type *parm)
{
printf("\n %d\n", parm->a);
}
Современные компиляторы языка С позволяют передавать структуры в качестве аргументов функций. Изменим вышеприведенную программу так, чтобы она давала возможность печатать сумму двух целых чисел и заданный символ:
#include <stdio.h>
#include <conio.h>
// Определение глобального типа структуры
struct struct_type {int a, b; char ch; };
// Прототип функции
void fun(struct struct_type STRUCT3);
int main (void)
{
struct struct_type struct2 = {2, 3, 'Z' };
fun(struct2);
printf("\n Press any key: ");
_getch();
return 0; }
// Определение функции
void fun(struct struct_type STRUCT3)
{
printf("\n %c: %d + %d = %d\n", \
STRUCT3.ch, STRUCT3.a, STRUCT3.b, STRUCT3.a + STRUCT3.b);
}
В современных версиях языка С, в том числе и ANSI C, структуры не только можно передавать функции в качестве аргументов, но и возвращать в качестве возвращаемого значения [15.3]. Использование структур в качестве аргументов функции позволяет передавать функции информацию о структуре. Использование функций для возврата структур позволяет передавать информацию из вызываемой функции в вызывающую функцию. Указатели на структуры допускают также
Пример 1. Напишите программу передачи частей структуры в качестве аргументов функции.
Рассмотрим пример подсчета суммы двух вещественных чисел, которые вводятся с клавиатуры для двух пользователей, которые задаются своими именами.
Программный код решения примера:
#include <stdio.h>
#include <conio.h>
#define MAX 20
// Шаблон структуры
struct test {char A[MAX+1]; double ax; char B[MAX+1];
double by;
};
//Прототип вспомогательной функции
double sum(double, double);
// Главная функция
int main (void) {
// Создание структурной переменной
struct test AB;
//Заполнение полей структуры
printf("\n\t Type 1 st name: ");
gets_s(AB.A, MAX);
printf("\t Enter the first real number: ");
scanf_s("%lf", AB.ax);
_flushall();
printf("\n\t Enter 2 nd name: ");
gets_s(AB.B, MAX);
printf("\t Enter the second real number: ");
scanf_s("%lf", AB.by);
printf("\n\t The sum of two numbers \
%1.2f and %1.2f, %s and %s: %1.2f\n",
AB.ax, AB.by, AB.A, AB.B, sum(AB.ax, AB.by));
printf("\n Press any key: ");
_getch();
return 0; }
// Функция суммирования двух чисел
double sum(double x, double y) {
return (x + y);
}
Возможный результат выполнения программы показан на рис 15.1.
(рис 15.1) Сумма двух чисел, переданных в качестве аргументов функцииЗадание 1
sum().Пример 2. Напишите программу выполнения арифметических действий с комплексными числами на основе структурного типа данных и печати результатов выполненных действий с помощью вспомогательной функции.
Для решения данного примера следует указать на действия с комплексными числами, заданными в алгебраической форме [15.4].
Суммой двух комплексных чисел $$z_1 = x_1 + y_1i$$ и $$z_2 = x_2 + y_2i$$ называется число $$z = x + yi$$ такое, что справедливы равенства $$х=х_1 + х_2$$, $$у = у_1 + у_2$$, т.е.
$$z = (x_1 + x_2) + (y_1 + y_2)i, где\ i – мнимая\ единица (i=\sqrt-1).$$Правило сложения. При сложении комплексных чисел складываются действительные и мнимые части соответственно.
Разностью чисел $$z_1$$ и $$z_2$$ называется число $$z$$ такое, что $$z_1 = z – z_2$$.
Правило вычитания. При нахождении разности $$z_1 – z_2$$ из действительной и мнимой частей уменьшаемого $$z_1$$ вычитаются соответственно действительная и мнимая части вычитаемого:
$$z = (x_1 – x_2) + (y_1 – y_2)i.$$Произведением двух комплексных чисел $$z_1 = x_1 + y_1i$$ и $$z_2 = x_2 + y_2i$$ называется число $$z = x + yi$$ такое, что выполняются равенства:
$$х = х_{1х2} – у_1у_2,$$ $$у = х_{1у2} + х_2у_1.$$Правило умножения. Комплексные числа перемножаются как двучлены, при этом учитывается: $$i^2 = –1$$.
Частным от деления числа $$z_1$$ на $$z_2 (z_2 \neq 0)$$ называется число $$z$$, такое, что справедливо равенство $$z\cdot z_2 = z_1$$.
Правило деления. Чтобы разделить число $$z_1$$ на $$z_2 (z_2 \neq 0)$$, следует числитель и знаменатель дроби $$z_1/z_2$$ умножить на число, сопряженное знаменателю.
Комплексные числа называются сопряженными, если у них равны действительные части, а мнимые противоположны по знаку.
Программный код решения примера:
#include <stdio.h>
#include <conio.h>
#include <locale.h>
// Шаблон структуры
struct comp {
long double Re;
long double Im;
};
// Прототип функции с аргументами:
// структура и симольная переменная
void complex (struct comp ri[2], char op);
int main (void) {
long double x, y;
char op;
// Определение структурной переменной
struct comp ri[2];
setlocale(LC_ALL, ".1251"); // для русских шрифтов
//Для определения чисел с десятичной точкой
setlocale(LC_NUMERIC, "English");
printf("\n\t ДЕЙСТВИЯ С КОМПЛЕКСНЫМИ ЧИСЛАМИ\n");
printf("\n Введите действительную часть 1-го комплексного числа: ");
scanf_s("%lf", x); _flushall();
ri[0].Re = x;
printf(" Введите мнимую часть 1-го комплексного числа: ");
scanf_s("%lf", y); _flushall();
ri[0].Im = y;
printf("\n Введите действительную часть 2-го сомплексного числа: ");
scanf_s("%lf", x); _flushall();
ri[1].Re = x;
printf(" Введите мнимую часть 2-го комплексного числа: ");
scanf_s("%lf", y); _flushall();
ri[1].Im = y;
printf("\n Введите арифметический оператор: ");
scanf_s("%c", op); _flushall();
//Вызов функции расчета комплексных чисел
printf("\n Результат действия (\"%c\") над двумя комплексными числами\n (результат с десятичной запятой):", op);
// для русских шрифтов с числами с десятичной запятой
setlocale(LC_ALL, ".1251");
complex (ri, op);
//Для определения числа с десятичной точкой
setlocale(LC_NUMERIC, "English");
//Вызов функции расчета комплексных чисел
printf("\n Результат действия (\"%c\") над двумя комплексными числами\n (результат с десятичной точкой):", op);
complex (ri, op);
printf("\n Нажмите любую клавишу (Press any key): ");
_getch();
return 0;
}
// Вспомогательная функция
void complex (struct comp ri[2], char z) {
// Вспомогательные переменные
double num1, num2, den;
// Выбор арифметического действия
switch (z) {
case '+' :
if ((ri[0].Im + ri[1].Im) >= 0)
printf("\n\t %1.4f + %1.4fi\n", ri[0].Re + ri[1].Re, ri[0].Im + ri[1].Im);
if ((ri[0].Im + ri[1].Im) < 0)
printf("\n\t %1.4f - %1.4fi\n", ri[0].Re + ri[1].Re, -(ri[0].Im + ri[1].Im));
break;
case '-' :
if ((ri[0].Im - ri[1].Im) >= 0)
printf("\n\t %1.4f + %1.4fi\n", ri[0].Re - ri[1].Re, ri[0].Im - ri[1].Im);
if ((ri[0].Im - ri[1].Im) < 0)
printf("\n\t %1.4f - %1.4fi\n", ri[0].Re - ri[1].Re, -(ri[0].Im - ri[1].Im));
break;
case '*' :
if ((ri[0].Re*ri[1].Im + ri[1].Re*ri[0].Im) >= 0)
printf("\n\t %1.4f + %1.4fi\n",\
ri[0].Re * ri[1].Re - ri[0].Im*ri[1].Im, ri[0].Re*ri[1].Im + ri[1].Re*ri[0].Im);
if ((ri[0].Re*ri[1].Im + ri[1].Re*ri[0].Im) < 0)
printf("\n\t %1.4f - %1.4fi\n",\
ri[0].Re * ri[1].Re - ri[0].Im*ri[1].Im, -(ri[0].Re*ri[1].Im + ri[1].Re*ri[0].Im));
break;
case '/' :
if (ri[1].Re != 0 ri[1].Im != 0) {
den = ri[1].Re*ri[1].Re + ri[1].Im*ri[1].Im;
num1 = (ri[0].Re*ri[1].Re - ri[0].Im*(-ri[1].Im))/den;
num2 = (ri[0].Re*(-ri[1].Im) + ri[1].Re*ri[0].Im)/den;
if (num2 >= 0)
printf("\n\t %1.4f + %1.4fi\n",num1, num2);
if (num2 < 0)
printf("\n\t %1.4f - %1.4fi\n",num1, -num2);
}
else
printf("\n\t Ошибка! Деление на нуль.\n");
break;
default : printf("\n\t Ошибка! Неизвестный оператор.\n");
break;
}
}
Возможный результат выполнения программы показан на рис 15.2.
(рис 15.2) Результат действия над двумя комплексными числамиЗадание 2
Пример 3. Напишите программу имитатора случайного таймера с помощью указателей на структуру [15.2].
Программный код решения примера:
#include <stdio.h>
#include <conio.h>
#include <time.h>
#include <stdlib.h>
// Глобальный шаблон структуры
struct mtime
{
int hours;
int minutes;
int seconds;
};
// Прототипы вспомогательных функций
void update (struct mtime *t);
void display (struct mtime *t);
// Главная функция
int main (void)
{
int i;
//Создание переменной структурного типа
struct mtime systime;
// Для изменения псевдослучайной последовательности
srand((unsigned) (long)time(NULL));
// Начальная инициализация структурной переменной
systime.hours = 0;
systime.minutes = 0;
systime.seconds = 0;
// Цикл вывода расчетного времени
for (i = 0; i < 10; ++i)
{
update(systime);
display(systime);
}
printf("\n\n Press any key: ");
_getch();
return 0;
}
// 1-я вспомогательная функция
void update (struct mtime *t) {
// Заполнение полей структуры случайными числами
t -> hours = (int)(24*rand()/RAND_MAX);
t -> minutes = (int)(60*rand()/RAND_MAX);
t -> seconds = (int)(60*rand()/RAND_MAX);
// Условия расчетного времени
t -> seconds += 1;
if (t -> seconds == 60)
{ t -> seconds = 0; t -> minutes++; }
if (t -> minutes == 60)
{ t -> minutes = 0; t -> hours++; }
if (t -> hours == 24)
t -> hours = 0;
}
// 2-я вспомогательная функция
void display (struct mtime *t) {
printf("\n System time: ");
printf("%02d:", t -> hours);
printf("%02d:", t -> minutes);
printf("%02d\n", t -> seconds);
}
В программе аргументами функций update() и display() являются указатели на структуру с дескриптором (этикеткой) mtime. При этом в главной функции main() создается структурная переменная systime по шаблону struct mtime и она передается в функции update() и display() через свой адрес, т.е. systime. Так происходит 10 раз (в цикле).
Примечание. В прототипе функций указатели на структуру могут быть записаны в обезличенной форме, например:
void update (struct mtime *); void display (struct mtime *);
Возможный результат выполнения программы показан на рис 15.3
(рис 15.3) Пример вывода полей структуры на консоль Задание 3
systime.Пример 4. Напишите программу, в которой используются функции, имеющие на входе структуру и возвращающие структуру.
Напишем программу расчета средней успеваемости студента за последнюю экзаменационную сессию (или другим экзаменам) с использованием структурного типа данных.
Программный код решения примера:
#include <stdio.h>
#include <conio.h>
#define MAX 79
// Шаблон структуры
struct table {
char name[MAX+1];
char surname[MAX+1];
char subject_1[MAX+1];
char subject_2[MAX+1];
char subject_3[MAX+1];
char subject_4[MAX+1];
int mark[4];
float mean;
} student = { // Инициализация структурной переменной
"Peter",
"Bobrov",
"Mathematics",
"Informatics",
"Programming",
"Physics",
{0,0,0,0},
0.0f };
// Прототип вспомогательной функции
struct table infor(struct table student);
// Главная функция
int main (void)
{
// Объявление и инициализация структурной переменной
struct table infor2 = {" "," "," "," "," "," ", {0,0,0,0},0.0f};
// Присвоение структуры от функции
infor2 = infor(student);
printf("\n The level of knowledge a student of %s %s is: \
%1.2f\n", infor2.name, infor2.surname, infor2.mean);
printf("\n\n Press any key: ");
_getch();
return 0;
}
// Вспомогательная функция с аргументом структурного типа
// и возвращающая структуру
struct table infor(struct table student) {
float x = 0.0f;
int i;
printf("\n\t Enter a name: ");
gets_s(student.name, MAX);
printf("\t Enter a surname: ");
gets_s(student.surname, MAX);
printf("\t Enter 1-st academic subject: ");
gets_s(student.subject_1, MAX);
printf("\t Enter a mark in the first subject: ");
scanf_s("%d",student.mark[0]);
_flushall();
printf("\t Enter 2-nd academic subject: ");
gets_s(student.subject_2, MAX);
printf("\t Enter a mark in the second subject: ");
scanf_s("%d",student.mark[1]);
_flushall();
printf("\t Enter 3-rd academic subject: ");
gets_s(student.subject_3, MAX);
printf("\t Enter a mark in the third subject: ");
scanf_s("%d",student.mark[2]);
_flushall();
printf("\t Enter 4-th academic subject: ");
gets_s(student.subject_4, MAX);
printf("\t Enter a mark in the fourth subject: ");
scanf_s("%d",student.mark[3]);
// Расчет средней оценки по 4 предметам
for (i = 0; i < 4; ++i) {
x += student.mark[i];
student.mean = x/4;
}
// Возвращение структурной переменной
return (student);
}
В программе приведена инициализация структурной переменной student для наглядности.
Инициализация переменной типа float выполнена с суффиксом f.
Присвоение одной структурной переменной другой может быть только в случае, когда они принадлежат одному и тому же шаблону структур.
Функции _flushall() введены для того, чтобы устранить пустую строку перед использованием функции gets_s() (или gets() ) после ввода с помощью функции scanf_s (или scanf() ).
Возможный результат выполнения программы показан на рис 15.4.
(рис 15.4) Пример расчета средней успеваемости студентаstudent.student в аргументе вспомогательной функции используйте собственную фамилию.main() всех полей структуры.typedef при создании шаблона структуры.Пример 5. Напишите программу, в которой функция возвращает указатель на структуру при поиске служебных слов языка С, вводимых с клавиатуры пользователем [3;8].
Программный код решения примера:
#include <stdio.h>
#include <conio.h>
#include <string.h>
#define MAX 1000
// Функция ввода строки с клавиатуры
void getLine(char str[], int m) {
int c, i;
for (i = 0; i < m-1 (c = getchar())!= EOF c != '\n'; i++)
str[i] = c;
// После цикла
str[i] = '\0'; // символ окончания строки
}
// Создание структуры глобального типа
struct key
{
char *keyword;
//int keycount;
} tab[] = {
"for",
"while",
"do",
"if",
"else",
"switch",
"case",
"break",
"default"
}, *bam;// указатель на структуру
// Вспомогательная функция
// с указателем на структуру key
struct key *PTR_ANALYSE(char *word, struct key tab[], int n)
{
int i;
struct key *PTR;
for (i = 0; i < n; ++i)
if (strcmp(tab[i].keyword, word) == 0) {
PTR = tab[i];
// возвращение указателя на структуру типа key
return (PTR); }
return NULL; // служебное слово не найдено
}
// Главная функция
int main (void)
{
int c;
char str[MAX];
printf("\n The analysis of input of syntactic words\n of the programming language C\n\n");
printf("\n The end of the session: press Ctrl+Z after pressing Enter\n\n");
do {
printf("\t Enter a new line: ");
getLine(str, MAX);
printf(" ");
bam = PTR_ANALYSE(str, tab, 9); // 9 - число слов
if (bam != NULL)
printf("\t String found: %s\n press Enter to continue or Ctrl+Z to quit: ", bam->keyword);
else
printf("\t STRING NOT FOUND:\n press Enter to continue or Ctrl+Z to quit: ");
} while ((c = getchar()) != EOF);
printf("\n Press any key: ");
_getch();
return 0;
}
В программе ввод слов осуществляется посимвольно с помощью специальной функции getLine().
Шаблон структуры – это struct key, для которого определяется переменная tab[] как массив структур. Одновременно определяется указатель на эту структуру, т.е. *bam.
Функция struct key *PTR_ANALYSE() возвращает указатель на структуру. Параметрами этой функции являются указатель на тип char, массив структур и целая переменная. Цикл do – while применен для того, чтобы тело цикла выполнялось хотя бы один раз. Массив структур с именем tab[] инициализирован служебными словами – struct key *PTR_ANALYSE() присваивается указателю *bam, который определен по шаблону глобальной структуры.
Возможный результат выполнения программы показан на рис 15.5.
(рис 15.5) Пример интерактивного поиска служебных словЗадание 5
key.Пример 6. Напишите программу сравнения введенного целого числа с имеющимися целыми случайными числами одномерного массива на основе структурного типа данных и двоичного поиска (
Программный код решения примера:
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <time.h>
#include <locale.h>
#define MAX 1000
// Функция ввода числа как строки с клавиатуры
void getLine(int num[], int m) {
int i, j, k;
time_t tic;
srand((unsigned) time(tic));
for (i = 0; i < m; ++i)
num[i] = (int)m*rand()/RAND_MAX;
printf("\n\t The initial array of numeric data:\n");
for (i = 0; i < m; ++i)
printf(" %3d", num[i]);
puts(" ");
// Сортировка массива по возрастанию
for (i = 1; i < m; ++i) {
for (j = 0; j < m-1; ++j) {
if (num[j] > num[j+1]) {
k = num[j];
num[j] = num[j+1];
num[j+1] = k;
}
}
}
printf("\t Assorted array of numerical data:\n");
printf(" ");
for (i = 0; i < m; ++i)
printf(" %3d", num[i]);
puts(" ");
}
// Создание структуры глобального типа
struct numb {
int index;
int numbers;
} tab, *bam;// указатель на структуру
// Вспомогательная функция
// с указателем на структуру numb
struct numb *PTR_ANALYSE(int x, int mass[], int n) {
int min = 0;
int max = n - 1;
int mid;
struct numb *PTR = tab;
// Бинарный поиск
while (min <= max) {
mid = (max + min)/2; // переход в середину массива
if (x == mass[mid]) {
PTR->numbers = mass[mid];
PTR->index = mid;
return (PTR); // возвращение указателя на структуру
}
else if (x < mass[mid])
max = mid - 1;
else
min = mid + 1;
} // End while
return NULL; // число не найдено
} // End function
// Главная функция
int main (void) {
int c, x;
int N, arr[MAX];
// Русские шрифты
setlocale(LC_ALL, ".1251");
printf("\n УГАДЫВАНИЕ ЧИСЛА В ЧИСЛОВОМ ОТСОРТИРОВАННОМ МАССИВЕ\n");
// Английские шрифты
setlocale(LC_ALL, "English");
printf("\n The end of the session: press Ctrl+Z after pressing Enter\n");
printf("\n\t Enter the dimension of the array of more than 3: ");
scanf_s("%d", N);
_flushall();
getLine(arr, N);
puts(" ");
do {
printf("\t Enter an integer: ");
scanf_s("%d", x);
_flushall();
printf(" ");
bam = PTR_ANALYSE(x, arr, N);
if (bam != NULL) {
printf("\t The number is found: %d\n ", bam->numbers);
printf("\t The index number of assorted array: %d\n", bam->index+1);
printf("\n\t Press Enter to continue or Ctrl+Z to quit: ");
}
else
printf("\t The number is not found.\n Press Enter to continue or Ctrl+Z to quit: ");
} while ((c = getchar()) != EOF);
printf("\n Press any key: ");
_getch();
return 0;
}
При двоичном алгоритме поиска после каждого сравнения исключается половина элементов массива, в котором производится поиск 15.5. Алгоритм находит средний элемент массива и сравнивает его с ключом поиска (в программе переменная х ). Если они равны, ключ поиска считается найденным и возвращается индекс этого элемента. Если они не равны, задача упрощается до поиска в одной половине массива.
Если ключ поиска меньше среднего элемента массива, поиск производится в первой половине массива. В противном случае поиск производится во второй половине. Если ключ поиска в указанном подмассиве не найден, алгоритм повторяется для четверти массива. Поиск продолжается до тех пор, пока ключ не окажется равен среднему элементу подмассива или пока подмассив не будет состоять из одного элемента, не равного ключу (это означает, что ключ поиска не найден).
Возможный результат выполнения программы показан на рис 15.6.
(рис 15.6) Результат двоичного поиска по совпадению ключейЗадание 6
Разрешенными операциями над структурами являются копирование или присваивание структуры как целого, взятие ее адреса операцией , а также обращение к ее элементам [15.1]. Копирование и присваивание включает в себя также передачу аргументов в функции и возвращение значений из функций.
Возможна передача членов структур функциям и передача целых структур функциям. При передаче функции члена структуры передается его значение, притом не играет роли то, что значение берется из члена структуры. Например [15.2], пусть задана структура следующего вида:
struct fred
{
char x;
int y;
float z;
char str[10]; // с учетом символа окончания строки
} mike;
Тогда каждый член этой структуры можно передать функции, например,
func(mike.x); // передается символьное значение х func2(mike.y);// передается целое значение y func3(mike.z);// передается значение с плавающей точкой z func4(mike.str);// передается адрес строки str[10] func(mike.str[2]);// передается символьное значение str[2]
Если же нужно передать адрес отдельного члена структуры, то перед именем структуры должен находиться оператор . Для рассмотренных примеров будем иметь
func(mike.x); // передается адрес символа х func2(mike.y);// передается адрес целого y func3(mike.z);// передается адрес члена z с плавающей точкой func4(mike.str);// передается адрес строки str func(mike.str[2]);// передается адрес символа в str[2]
Когда в качестве аргумента функции используется структура, для передачи целой структуры используется обычный способ вызова по значению [15.2]. Это означает, что любые изменения в содержимом параметра внутри функции не отразятся на той структуре, которая передана в качестве аргумента.
При использовании структуры в качестве параметра надо помнить, что тип аргумента должен соответствовать типу параметра. В качестве примера рассмотрим следующий программный код [15.2]:
#include <stdio.h>
#include <conio.h>
// Определение глобального типа структуры
struct struct_type {
int a, b;
char ch;
};
// Прототип функции
void fun(struct struct_type parm);
int main (void) {
struct struct_type arg;
arg.a = 1000;
fun(arg);
printf("\n Press any key: ");
_getch();
return 0;
}
// Определение функции
void fun(struct struct_type parm)
{
printf("\n %d\n", parm.a);
}
Назначение этой программы состоит в печати заданных полей структуры, которая объявлена как глобальная, чтобы структурный тип был виден во всей программе.
Когда используется указатель на структуру, вместо оператора точки используется оператор "стрелка". Видоизменим рассмотренную программу, введем в нее указатель на структуру:
#include <stdio.h>
#include <conio.h>
// Определение глобального типа структуры
struct struct_type { int a, b; char ch; };
// Прототип функции
void fun(struct struct_type *parm);
int main (void)
{
struct struct_type arg,*PTR;
PTR = arg;
PTR->a = 999;
fun(PTR);
printf("\n Press any key: ");
_getch();
return 0;
}
// Определение функции
void fun(struct struct_type *parm)
{
printf("\n %d\n", parm->a);
}
Современные компиляторы языка С позволяют передавать структуры в качестве аргументов функций. Изменим вышеприведенную программу так, чтобы она давала возможность печатать сумму двух целых чисел и заданный символ:
#include <stdio.h>
#include <conio.h>
// Определение глобального типа структуры
struct struct_type {int a, b; char ch; };
// Прототип функции
void fun(struct struct_type STRUCT3);
int main (void)
{
struct struct_type struct2 = {2, 3, 'Z' };
fun(struct2);
printf("\n Press any key: ");
_getch();
return 0; }
// Определение функции
void fun(struct struct_type STRUCT3)
{
printf("\n %c: %d + %d = %d\n", \
STRUCT3.ch, STRUCT3.a, STRUCT3.b, STRUCT3.a + STRUCT3.b);
}
В современных версиях языка С, в том числе и ANSI C, структуры не только можно передавать функции в качестве аргументов, но и возвращать в качестве возвращаемого значения [15.3]. Использование структур в качестве аргументов функции позволяет передавать функции информацию о структуре. Использование функций для возврата структур позволяет передавать информацию из вызываемой функции в вызывающую функцию. Указатели на структуры допускают также
Пример 1. Напишите программу передачи частей структуры в качестве аргументов функции.
Рассмотрим пример подсчета суммы двух вещественных чисел, которые вводятся с клавиатуры для двух пользователей, которые задаются своими именами.
Программный код решения примера:
#include <stdio.h>
#include <conio.h>
#define MAX 20
// Шаблон структуры
struct test {char A[MAX+1]; double ax; char B[MAX+1];
double by;
};
//Прототип вспомогательной функции
double sum(double, double);
// Главная функция
int main (void) {
// Создание структурной переменной
struct test AB;
//Заполнение полей структуры
printf("\n\t Type 1 st name: ");
gets_s(AB.A, MAX);
printf("\t Enter the first real number: ");
scanf_s("%lf", AB.ax);
_flushall();
printf("\n\t Enter 2 nd name: ");
gets_s(AB.B, MAX);
printf("\t Enter the second real number: ");
scanf_s("%lf", AB.by);
printf("\n\t The sum of two numbers \
%1.2f and %1.2f, %s and %s: %1.2f\n",
AB.ax, AB.by, AB.A, AB.B, sum(AB.ax, AB.by));
printf("\n Press any key: ");
_getch();
return 0; }
// Функция суммирования двух чисел
double sum(double x, double y) {
return (x + y);
}
Возможный результат выполнения программы показан на рис 15.1.
(рис 15.1) Сумма двух чисел, переданных в качестве аргументов функцииЗадание 1
sum().Пример 2. Напишите программу выполнения арифметических действий с комплексными числами на основе структурного типа данных и печати результатов выполненных действий с помощью вспомогательной функции.
Для решения данного примера следует указать на действия с комплексными числами, заданными в алгебраической форме [15.4].
Суммой двух комплексных чисел $$z_1 = x_1 + y_1i$$ и $$z_2 = x_2 + y_2i$$ называется число $$z = x + yi$$ такое, что справедливы равенства $$х=х_1 + х_2$$, $$у = у_1 + у_2$$, т.е.
$$z = (x_1 + x_2) + (y_1 + y_2)i, где\ i – мнимая\ единица (i=\sqrt-1).$$Правило сложения. При сложении комплексных чисел складываются действительные и мнимые части соответственно.
Разностью чисел $$z_1$$ и $$z_2$$ называется число $$z$$ такое, что $$z_1 = z – z_2$$.
Правило вычитания. При нахождении разности $$z_1 – z_2$$ из действительной и мнимой частей уменьшаемого $$z_1$$ вычитаются соответственно действительная и мнимая части вычитаемого:
$$z = (x_1 – x_2) + (y_1 – y_2)i.$$Произведением двух комплексных чисел $$z_1 = x_1 + y_1i$$ и $$z_2 = x_2 + y_2i$$ называется число $$z = x + yi$$ такое, что выполняются равенства:
$$х = х_{1х2} – у_1у_2,$$ $$у = х_{1у2} + х_2у_1.$$Правило умножения. Комплексные числа перемножаются как двучлены, при этом учитывается: $$i^2 = –1$$.
Частным от деления числа $$z_1$$ на $$z_2 (z_2 \neq 0)$$ называется число $$z$$, такое, что справедливо равенство $$z\cdot z_2 = z_1$$.
Правило деления. Чтобы разделить число $$z_1$$ на $$z_2 (z_2 \neq 0)$$, следует числитель и знаменатель дроби $$z_1/z_2$$ умножить на число, сопряженное знаменателю.
Комплексные числа называются сопряженными, если у них равны действительные части, а мнимые противоположны по знаку.
Программный код решения примера:
#include <stdio.h>
#include <conio.h>
#include <locale.h>
// Шаблон структуры
struct comp {
long double Re;
long double Im;
};
// Прототип функции с аргументами:
// структура и симольная переменная
void complex (struct comp ri[2], char op);
int main (void) {
long double x, y;
char op;
// Определение структурной переменной
struct comp ri[2];
setlocale(LC_ALL, ".1251"); // для русских шрифтов
//Для определения чисел с десятичной точкой
setlocale(LC_NUMERIC, "English");
printf("\n\t ДЕЙСТВИЯ С КОМПЛЕКСНЫМИ ЧИСЛАМИ\n");
printf("\n Введите действительную часть 1-го комплексного числа: ");
scanf_s("%lf", x); _flushall();
ri[0].Re = x;
printf(" Введите мнимую часть 1-го комплексного числа: ");
scanf_s("%lf", y); _flushall();
ri[0].Im = y;
printf("\n Введите действительную часть 2-го сомплексного числа: ");
scanf_s("%lf", x); _flushall();
ri[1].Re = x;
printf(" Введите мнимую часть 2-го комплексного числа: ");
scanf_s("%lf", y); _flushall();
ri[1].Im = y;
printf("\n Введите арифметический оператор: ");
scanf_s("%c", op); _flushall();
//Вызов функции расчета комплексных чисел
printf("\n Результат действия (\"%c\") над двумя комплексными числами\n (результат с десятичной запятой):", op);
// для русских шрифтов с числами с десятичной запятой
setlocale(LC_ALL, ".1251");
complex (ri, op);
//Для определения числа с десятичной точкой
setlocale(LC_NUMERIC, "English");
//Вызов функции расчета комплексных чисел
printf("\n Результат действия (\"%c\") над двумя комплексными числами\n (результат с десятичной точкой):", op);
complex (ri, op);
printf("\n Нажмите любую клавишу (Press any key): ");
_getch();
return 0;
}
// Вспомогательная функция
void complex (struct comp ri[2], char z) {
// Вспомогательные переменные
double num1, num2, den;
// Выбор арифметического действия
switch (z) {
case '+' :
if ((ri[0].Im + ri[1].Im) >= 0)
printf("\n\t %1.4f + %1.4fi\n", ri[0].Re + ri[1].Re, ri[0].Im + ri[1].Im);
if ((ri[0].Im + ri[1].Im) < 0)
printf("\n\t %1.4f - %1.4fi\n", ri[0].Re + ri[1].Re, -(ri[0].Im + ri[1].Im));
break;
case '-' :
if ((ri[0].Im - ri[1].Im) >= 0)
printf("\n\t %1.4f + %1.4fi\n", ri[0].Re - ri[1].Re, ri[0].Im - ri[1].Im);
if ((ri[0].Im - ri[1].Im) < 0)
printf("\n\t %1.4f - %1.4fi\n", ri[0].Re - ri[1].Re, -(ri[0].Im - ri[1].Im));
break;
case '*' :
if ((ri[0].Re*ri[1].Im + ri[1].Re*ri[0].Im) >= 0)
printf("\n\t %1.4f + %1.4fi\n",\
ri[0].Re * ri[1].Re - ri[0].Im*ri[1].Im, ri[0].Re*ri[1].Im + ri[1].Re*ri[0].Im);
if ((ri[0].Re*ri[1].Im + ri[1].Re*ri[0].Im) < 0)
printf("\n\t %1.4f - %1.4fi\n",\
ri[0].Re * ri[1].Re - ri[0].Im*ri[1].Im, -(ri[0].Re*ri[1].Im + ri[1].Re*ri[0].Im));
break;
case '/' :
if (ri[1].Re != 0 ri[1].Im != 0) {
den = ri[1].Re*ri[1].Re + ri[1].Im*ri[1].Im;
num1 = (ri[0].Re*ri[1].Re - ri[0].Im*(-ri[1].Im))/den;
num2 = (ri[0].Re*(-ri[1].Im) + ri[1].Re*ri[0].Im)/den;
if (num2 >= 0)
printf("\n\t %1.4f + %1.4fi\n",num1, num2);
if (num2 < 0)
printf("\n\t %1.4f - %1.4fi\n",num1, -num2);
}
else
printf("\n\t Ошибка! Деление на нуль.\n");
break;
default : printf("\n\t Ошибка! Неизвестный оператор.\n");
break;
}
}
Возможный результат выполнения программы показан на рис 15.2.
(рис 15.2) Результат действия над двумя комплексными числамиЗадание 2
Пример 3. Напишите программу имитатора случайного таймера с помощью указателей на структуру [15.2].
Программный код решения примера:
#include <stdio.h>
#include <conio.h>
#include <time.h>
#include <stdlib.h>
// Глобальный шаблон структуры
struct mtime
{
int hours;
int minutes;
int seconds;
};
// Прототипы вспомогательных функций
void update (struct mtime *t);
void display (struct mtime *t);
// Главная функция
int main (void)
{
int i;
//Создание переменной структурного типа
struct mtime systime;
// Для изменения псевдослучайной последовательности
srand((unsigned) (long)time(NULL));
// Начальная инициализация структурной переменной
systime.hours = 0;
systime.minutes = 0;
systime.seconds = 0;
// Цикл вывода расчетного времени
for (i = 0; i < 10; ++i)
{
update(systime);
display(systime);
}
printf("\n\n Press any key: ");
_getch();
return 0;
}
// 1-я вспомогательная функция
void update (struct mtime *t) {
// Заполнение полей структуры случайными числами
t -> hours = (int)(24*rand()/RAND_MAX);
t -> minutes = (int)(60*rand()/RAND_MAX);
t -> seconds = (int)(60*rand()/RAND_MAX);
// Условия расчетного времени
t -> seconds += 1;
if (t -> seconds == 60)
{ t -> seconds = 0; t -> minutes++; }
if (t -> minutes == 60)
{ t -> minutes = 0; t -> hours++; }
if (t -> hours == 24)
t -> hours = 0;
}
// 2-я вспомогательная функция
void display (struct mtime *t) {
printf("\n System time: ");
printf("%02d:", t -> hours);
printf("%02d:", t -> minutes);
printf("%02d\n", t -> seconds);
}
В программе аргументами функций update() и display() являются указатели на структуру с дескриптором (этикеткой) mtime. При этом в главной функции main() создается структурная переменная systime по шаблону struct mtime и она передается в функции update() и display() через свой адрес, т.е. systime. Так происходит 10 раз (в цикле).
Примечание. В прототипе функций указатели на структуру могут быть записаны в обезличенной форме, например:
void update (struct mtime *); void display (struct mtime *);
Возможный результат выполнения программы показан на рис 15.3
(рис 15.3) Пример вывода полей структуры на консольЗадание 3
systime.Пример 4. Напишите программу, в которой используются функции, имеющие на входе структуру и возвращающие структуру.
Напишем программу расчета средней успеваемости студента за последнюю экзаменационную сессию (или другим экзаменам) с использованием структурного типа данных.
Программный код решения примера:
#include <stdio.h>
#include <conio.h>
#define MAX 79
// Шаблон структуры
struct table {
char name[MAX+1];
char surname[MAX+1];
char subject_1[MAX+1];
char subject_2[MAX+1];
char subject_3[MAX+1];
char subject_4[MAX+1];
int mark[4];
float mean;
} student = { // Инициализация структурной переменной
"Peter",
"Bobrov",
"Mathematics",
"Informatics",
"Programming",
"Physics",
{0,0,0,0},
0.0f };
// Прототип вспомогательной функции
struct table infor(struct table student);
// Главная функция
int main (void)
{
// Объявление и инициализация структурной переменной
struct table infor2 = {" "," "," "," "," "," ", {0,0,0,0},0.0f};
// Присвоение структуры от функции
infor2 = infor(student);
printf("\n The level of knowledge a student of %s %s is: \
%1.2f\n", infor2.name, infor2.surname, infor2.mean);
printf("\n\n Press any key: ");
_getch();
return 0;
}
// Вспомогательная функция с аргументом структурного типа
// и возвращающая структуру
struct table infor(struct table student) {
float x = 0.0f;
int i;
printf("\n\t Enter a name: ");
gets_s(student.name, MAX);
printf("\t Enter a surname: ");
gets_s(student.surname, MAX);
printf("\t Enter 1-st academic subject: ");
gets_s(student.subject_1, MAX);
printf("\t Enter a mark in the first subject: ");
scanf_s("%d",student.mark[0]);
_flushall();
printf("\t Enter 2-nd academic subject: ");
gets_s(student.subject_2, MAX);
printf("\t Enter a mark in the second subject: ");
scanf_s("%d",student.mark[1]);
_flushall();
printf("\t Enter 3-rd academic subject: ");
gets_s(student.subject_3, MAX);
printf("\t Enter a mark in the third subject: ");
scanf_s("%d",student.mark[2]);
_flushall();
printf("\t Enter 4-th academic subject: ");
gets_s(student.subject_4, MAX);
printf("\t Enter a mark in the fourth subject: ");
scanf_s("%d",student.mark[3]);
// Расчет средней оценки по 4 предметам
for (i = 0; i < 4; ++i) {
x += student.mark[i];
student.mean = x/4;
}
// Возвращение структурной переменной
return (student);
}
В программе приведена инициализация структурной переменной student для наглядности.
Инициализация переменной типа float выполнена с суффиксом f.
Присвоение одной структурной переменной другой может быть только в случае, когда они принадлежат одному и тому же шаблону структур.
Функции _flushall() введены для того, чтобы устранить пустую строку перед использованием функции gets_s() (или gets() ) после ввода с помощью функции scanf_s (или scanf() ).
Возможный результат выполнения программы показан на рис 15.4.
(рис 15.4) Пример расчета средней успеваемости студентаstudent.student в аргументе вспомогательной функции используйте собственную фамилию.main() всех полей структуры.typedef при создании шаблона структуры.Пример 5. Напишите программу, в которой функция возвращает указатель на структуру при поиске служебных слов языка С, вводимых с клавиатуры пользователем [3;8].
Программный код решения примера:
#include <stdio.h>
#include <conio.h>
#include <string.h>
#define MAX 1000
// Функция ввода строки с клавиатуры
void getLine(char str[], int m) {
int c, i;
for (i = 0; i < m-1 (c = getchar())!= EOF c != '\n'; i++)
str[i] = c;
// После цикла
str[i] = '\0'; // символ окончания строки
}
// Создание структуры глобального типа
struct key
{
char *keyword;
//int keycount;
} tab[] = {
"for",
"while",
"do",
"if",
"else",
"switch",
"case",
"break",
"default"
}, *bam;// указатель на структуру
// Вспомогательная функция
// с указателем на структуру key
struct key *PTR_ANALYSE(char *word, struct key tab[], int n)
{
int i;
struct key *PTR;
for (i = 0; i < n; ++i)
if (strcmp(tab[i].keyword, word) == 0) {
PTR = tab[i];
// возвращение указателя на структуру типа key
return (PTR); }
return NULL; // служебное слово не найдено
}
// Главная функция
int main (void)
{
int c;
char str[MAX];
printf("\n The analysis of input of syntactic words\n of the programming language C\n\n");
printf("\n The end of the session: press Ctrl+Z after pressing Enter\n\n");
do {
printf("\t Enter a new line: ");
getLine(str, MAX);
printf(" ");
bam = PTR_ANALYSE(str, tab, 9); // 9 - число слов
if (bam != NULL)
printf("\t String found: %s\n press Enter to continue or Ctrl+Z to quit: ", bam->keyword);
else
printf("\t STRING NOT FOUND:\n press Enter to continue or Ctrl+Z to quit: ");
} while ((c = getchar()) != EOF);
printf("\n Press any key: ");
_getch();
return 0;
}
В программе ввод слов осуществляется посимвольно с помощью специальной функции getLine().
Шаблон структуры – это struct key, для которого определяется переменная tab[] как массив структур. Одновременно определяется указатель на эту структуру, т.е. *bam.
Функция struct key *PTR_ANALYSE() возвращает указатель на структуру. Параметрами этой функции являются указатель на тип char, массив структур и целая переменная. Цикл do – while применен для того, чтобы тело цикла выполнялось хотя бы один раз. Массив структур с именем tab[] инициализирован служебными словами – struct key *PTR_ANALYSE() присваивается указателю *bam, который определен по шаблону глобальной структуры.
Возможный результат выполнения программы показан на рис 15.5.
(рис 15.5) Пример интерактивного поиска служебных словЗадание 5
key.Пример 6. Напишите программу сравнения введенного целого числа с имеющимися целыми случайными числами одномерного массива на основе структурного типа данных и двоичного поиска (
Программный код решения примера:
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <time.h>
#include <locale.h>
#define MAX 1000
// Функция ввода числа как строки с клавиатуры
void getLine(int num[], int m) {
int i, j, k;
time_t tic;
srand((unsigned) time(tic));
for (i = 0; i < m; ++i)
num[i] = (int)m*rand()/RAND_MAX;
printf("\n\t The initial array of numeric data:\n");
for (i = 0; i < m; ++i)
printf(" %3d", num[i]);
puts(" ");
// Сортировка массива по возрастанию
for (i = 1; i < m; ++i) {
for (j = 0; j < m-1; ++j) {
if (num[j] > num[j+1]) {
k = num[j];
num[j] = num[j+1];
num[j+1] = k;
}
}
}
printf("\t Assorted array of numerical data:\n");
printf(" ");
for (i = 0; i < m; ++i)
printf(" %3d", num[i]);
puts(" ");
}
// Создание структуры глобального типа
struct numb {
int index;
int numbers;
} tab, *bam;// указатель на структуру
// Вспомогательная функция
// с указателем на структуру numb
struct numb *PTR_ANALYSE(int x, int mass[], int n) {
int min = 0;
int max = n - 1;
int mid;
struct numb *PTR = tab;
// Бинарный поиск
while (min <= max) {
mid = (max + min)/2; // переход в середину массива
if (x == mass[mid]) {
PTR->numbers = mass[mid];
PTR->index = mid;
return (PTR); // возвращение указателя на структуру
}
else if (x < mass[mid])
max = mid - 1;
else
min = mid + 1;
} // End while
return NULL; // число не найдено
} // End function
// Главная функция
int main (void) {
int c, x;
int N, arr[MAX];
// Русские шрифты
setlocale(LC_ALL, ".1251");
printf("\n УГАДЫВАНИЕ ЧИСЛА В ЧИСЛОВОМ ОТСОРТИРОВАННОМ МАССИВЕ\n");
// Английские шрифты
setlocale(LC_ALL, "English");
printf("\n The end of the session: press Ctrl+Z after pressing Enter\n");
printf("\n\t Enter the dimension of the array of more than 3: ");
scanf_s("%d", N);
_flushall();
getLine(arr, N);
puts(" ");
do {
printf("\t Enter an integer: ");
scanf_s("%d", x);
_flushall();
printf(" ");
bam = PTR_ANALYSE(x, arr, N);
if (bam != NULL) {
printf("\t The number is found: %d\n ", bam->numbers);
printf("\t The index number of assorted array: %d\n", bam->index+1);
printf("\n\t Press Enter to continue or Ctrl+Z to quit: ");
}
else
printf("\t The number is not found.\n Press Enter to continue or Ctrl+Z to quit: ");
} while ((c = getchar()) != EOF);
printf("\n Press any key: ");
_getch();
return 0;
}
При двоичном алгоритме поиска после каждого сравнения исключается половина элементов массива, в котором производится поиск 15.5. Алгоритм находит средний элемент массива и сравнивает его с ключом поиска (в программе переменная х ). Если они равны, ключ поиска считается найденным и возвращается индекс этого элемента. Если они не равны, задача упрощается до поиска в одной половине массива.
Если ключ поиска меньше среднего элемента массива, поиск производится в первой половине массива. В противном случае поиск производится во второй половине. Если ключ поиска в указанном подмассиве не найден, алгоритм повторяется для четверти массива. Поиск продолжается до тех пор, пока ключ не окажется равен среднему элементу подмассива или пока подмассив не будет состоять из одного элемента, не равного ключу (это означает, что ключ поиска не найден).
Возможный результат выполнения программы показан на рис 15.6.
(рис 15.6) Результат двоичного поиска по совпадению ключейЗадание 6
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.