В предыдущей лабораторной работе рассматривались примеры функций, аргументами которых выступали указатели. В данной лабораторной работе подробнее будут рассмотрены вопросы, касающиеся указателей и функций.
Ранее было отмечено, что в языке С аргументы передаются в функции по значению и не существует прямого способа изменить переменную вызывающей функции, действуя внутри вызываемой функции. Благодаря аргументам-указателям функция может обращаться к объектам в вызвавшей ее функции, в том числе модифицировать их [11.1]. В качестве примера рассмотрим функцию swap(), в задачу которой входит обмен элементов местами. Для решения такой задачи необходимо передать из вызывающей программы (например, из главной функции main() ) в функцию указатели на переменные, которые нужно изменить. Программный код решения примера:
#include <stdio.h>
#include <conio.h>
// Прототип функции
void swap(int*, int*);
int main (void) {
int a = 10,
b = -20;
// Вывод на консоль исходных значений переменных
printf("\n Initial values:\n a = %d, b = %d\n", a, b);
// Вызов функции swap() с фактическими параметрами
swap(a, b);
// Результат после обращения функции swap()
printf("\n New values:\n a = %d, b = %d\n", a, b);
printf("\n ... Press any key: ");
_getch();
return 0;
}
// Определение функции
void swap(int *pa, int *pb)
{
int temp;
temp = *pa;
*pa = *pb;
*pb = temp;
}
В программе в качестве фактических параметров функции swap() выступают адреса заданных переменных. Можно было в главной функции определить указатели и инициализировать их адресами заданных переменных, а потом передать эти указатели в функцию swap.
Результат выполнения программы показан нa рис 11.1.
(рис 11.1) Результат обмена данными, выполненного функцией swap()Указатели, передаваемые в функцию, могут быть указателями на указатели. Указатели могут указывать на начало какого-либо массива и т. д. Указатели могут использоваться для защиты массивов, над которыми необходимо произвести некоторые вычисления или преобразования.
Особым свойством указателей можно считать возможность использовать их в качестве возвращаемых значений функций. Поскольку функции возвращают только одно значение, то несколько значений одного типа можно поместить в массив, а затем указатель на этот массив использовать в качестве возвращаемого значения.
Общая форма определения функции, которая возвращает указатель, следующая:
тип *имя_функции ( аргументы функции )
{
// тело функции
тип *имя_указателя;
?
return имя_указателя;
}
Рассмотрим пример, в котором осуществляется сложение двух одномерных массивов и результат возвращается через указатель.
Программный код решения примера:
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
int *out2(int A[], int B[], int);
int main (void) {
int i, n;
int A[] = {1,2,3,4,5};
int B[] = {2,2,2,2,2};
int *ptrAB = NULL;
n = (sizeof(A)/sizeof(A[0]));
puts("\n The initial arrays: ");
for (i = 0; i < n; i++)
printf(" %d", A[i]);
puts("");
for (i = 0; i < n; i++)
printf(" %d", B[i]);
ptrAB = out2(A, B, n);
puts("\n\n Result from function: ");
for (i = 0; i < n; i++)
printf(" %d", ptrAB[i]);
puts("\n\n Control of the arrays: ");
for (i = 0; i < n; i++)
printf(" %d", A[i]);
puts("");
for (i = 0; i < n; i++)
printf(" %d", B[i]);
free(ptrAB); // освобождение выделенной памяти
printf("\n\n ... Press any key: ");
_getch();
return 0;
}
int *out2(int A[], int B[], int n)
{
int i;
int *ptr = (int *)calloc(n, sizeof(int)); //выделение памяти
for (i = 0; i < n; i++)
ptr[i] = A[i] + B[i];
return ptr;
}
Программа не требует особых пояснений.
Следует отметить, что никогда не следует возвращать
Указатели возвращаются подобно значениям любых других типов данных. Чтобы вернуть указатель, функция должна объявить его тип в качестве типа возвращаемого значения. Таким образом, если функция возвращает указатель, то значение, используемое в ее инструкции return, также должно быть указателем. В частности, многие библиотечные функции, предназначенные для обработки строк, возвращают указатели на символы.
В языке С существует такой механизм как указатель на функцию. Допустим, существует несколько функций для различных операций с данными. В этом случае оказывается удобным определить указатель на функцию, и использовать его там, где требуется производить расчет для различных функций.
Указатель на функцию – это переменная, содержащая адрес в памяти, по которому расположена функция [11.2]. Имя функции – это адрес начала программного кода функции. Указатели на функции могут быть переданы функциям в качестве аргументов, могут возвращаться функциями, сохраняться в массивах и присваиваться другим указателям на функции [11.2].
Типичное определение указателя на функцию следующее:
тип_возвращаемый_функцией(*имя_указателя_на_функцию)(аргументы);
В приведенном объявлении используются круглые скобки, в которых собственно и определяется указатель на функцию, которая возвращает тот или иной тип – тип_возвращаемый_функцией. Хотя знак * обозначает *имя_указателя_на_функцию. Очевидно, что возможны сложные объявлений функций.
Указатели на функции часто используются в системах, управляемых меню [11.2]. Пользователь выбирает команду меню (одну из нескольких). Каждая команда обслуживается своей функцией. Указатели на каждую функцию находятся в массиве указателей. Выбор пользователя служит индексом, по которому из массива выбирается указатель на нужную функцию.
Другим типичным применением указателей на функции являются реализация обобщенных алгоритмов, например, алгоритмов сортировки и поиска. В этом случае критерии сортировки и поиска реализуются в виде отдельных функций и передаются при помощи указателей на функции в качестве параметра реализации основного алгоритма.
Пример 1. Напишите программу с функцией
В условии примера "вызов по ссылке" означает, что в качестве фактических параметров функций будут использоваться
Программный код решения примера:
#include <stdio.h>
#include <conio.h>
// Прототип функции
void bsort (int* const, const int);
int main (void)
{
int A[] = {56, 34, 2, 0, 1, -21, 6, 8, 7};
int i, n;
//Размерность массива
n = sizeof(A)/sizeof(A[0]);
puts("\n Data items in original order:");
for (i = 0; i < n; i++)
printf(" %3d", A[i]);
// Вызов функции сортировки - bsort()
bsort (A, n);
puts("\n\n Data items in ascending order:");
for (i = 0; i < n; i++)
printf(" %3d", A[i]);
printf("\n\n ... Press any key: ");
_getch();
return 0;
}
// Определение функции
void swap(int *pa, int *pb)
{
int temp;
temp = *pa;
*pa = *pb;
*pb = temp;
}
void bsort (int *const arr, const int size)
{
int pass, //счетчик проходов
j; // счетчик сравнений
// Прототип функции обмена - swap()
void swap (int*, int*);
// Цикл для контроля проходов
for (pass = 0; pass < size - 1; pass++ )
{
// цикл для контроля сравнений на данном проходе
for (j = 0; j < size - 1; j++)
{
// обмен значений при нарушении порядка возрастания
if (arr[j] > arr[j + 1])
{
swap(arr[j], arr[j+1]);
}
}
}
}
В программе функция сортировки bsort() в качестве формального параметра используется константный указатель, который указывает на первый элемент заданного массива. Второй формальный параметр также константный, чтобы подчеркнуть неизменность этого параметра в теле функции bsort(). Передача функции размера массива в качестве параметра имеет два преимущества – это хороший стиль программирования и, кроме того, такую функцию можно использовать многократно.
Прототип функции swap() включен в тело функции bsort(), потому что это единственная функция, которая вызывает функцию обмена swap().
Пример выполнения программы показан на рис 11.2.
(рис 11.2) Пример сортировки массива методом пузырькаЗадание 1
bsort() возвращала указатель на отсортированный массив, а сам исходный массив был при этом неизменным. Предусмотрите вывод на консоль исходного массива, потом отсортированного массива после вызова функции сортировки, и снова для контроля исходный массив. При этом аргументы функции bsort() оставить без изменения.Пример 2. Напишите программу, в которой используется функция по расчету среднего значения (среднего арифметического) одномерного числового массива, его исправленной выборочной дисперсии и среднего квадратичного отклонения (main()
Приведем формулы, по которым рассчитываются среднее значение выборки, исправленная выборочная дисперсия и
Среднее выборочное значение одномерного массива размерностью N
$$mean=\frac 1 N \sum^N_{i=1}ai,$$где – элементы массива.
Исправленная выборочная дисперсия одномерного массива размерностью N
$$D=\frac 1 {N-1} \sum^N_{i=1}(a_i-mean)^2,$$где mean – среднее значение данного массива
Исправленное
где D – дисперсия данного массива.
Программный код решения примера:
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <math.h>
// Прототип функции
double *mean_D_S(int arr[], int n);
int main(void)
{
int mass[] = {2, -3, 5, 6, 7, 8, 9,-1};
int i, n;
double *R;
n = sizeof(mass)/sizeof(mass[0]);
puts("\n\t Initial array:");
for (i = 0; i < n; i++)
printf(" %3d", mass[i]);
// Вызов функции
R = mean_D_S(mass, n);
// Вывод расчтных характеристик массива
printf("\n\n The average value of an array: %g\n", R[0]);
printf(" The dispersion of the array: %g\n", R[1]);
printf(" The standard deviation of the array: %g\n", R[2]);
// Освобождение памяти
free(R);
printf("\n ... Press any key: ");
_getch();
return 0;
}
// Определение функции
double *mean_D_S(int arr[], int N)
{
int j;
double *PTR3 = (double *)calloc(N, sizeof(double));
double mean, D, S;
mean = 0.0;
for (j = 0; j < N; j++)
mean += arr[j];
mean /= N;
D = 0.0;
for (j = 0; j < N; j++)
D += (arr[j] - mean)*(arr[j] - mean);
D /= (N-1);
S = sqrt(D);
PTR3[0] = mean;
PTR3[1] = D;
PTR3[2] = S;
return PTR3;
}
В программе используется функция calloc(), которая выделенные ячейки памяти обнуляет. Расчетные характеристики одномерного массива размещаются последовательно друг за другом в выделенной памяти для указателя *PTR3. Сформированный указатель функция возвращает в точку вызова функции mean_D_S().
Результат выполнения программы показан на рис 11.3.
(рис 11.3) Расчет статистических характеристик числового массива Задание 2
mean_D_S() используйте указатель на числовой массив.mean_D_S() еще исходного массива, поэлементно возведенного в квадрат. В главной функции main() результаты выведите на консоль.float.Пример 3. Напишите программу с указателем на функции, которые рассчитывают следующие статистические характеристики одномерного числового массива: среднее арифметическое значение, медиану и модус (моду).
Среднее арифметическое рассчитывалось в предыдущем примере.
Приведем определения медианы и модуса, взятые из книги [11.3], из которой также взяты основные фрагменты программных кодов (в книге программы написаны на С++).
Медиана – это серединное значение в наборе данных – т. е. такое, что ровно половина значений располагается выше, и ровно половина ниже его.
Модус – это значение, наиболее часто встречающееся в наборе данных.
Программный код решения примера:
#include <stdio.h>
#include <conio.h>
double mean(int*, int);
double median(int*, int);
double mode(int*, int);
int main(void) {
int mass[] = {5, 6, 5, 5, 3,
6, 5, 3, 1, 4,
5, 3, 1, 6, 5,
2, 5, 2, 3, 4};
int *ptr = mass;
int temp, i, j, k, n;
// Указатель на функции
double (*fun[3])(int*, int) = {mean, median, mode};
n = sizeof(mass)/sizeof(mass[0]);
puts("\n The original array:");
j = 0;
for (i = 0; i < n; i++) {
j++;
if ( j%6 )
printf(" %2d", mass[i]);
else
{puts(""); printf(" %2d", mass[i]); j = 1;}
}
// Сортировка методом выбора
for (i = 0; i < (n - 1); ++i) {
temp = ptr[i]; k = i;
for (j = i + 1; j < n; ++j)
if (ptr[j] < temp) { k = j; temp = ptr[k]; }
ptr[k] = ptr[i]; ptr[i] = temp;
}
puts("\n\n Results - mean, mediana, modus: ");
for (i = 0; i < 3; i++)
printf("%6g\n",(*fun[i])(ptr, n));
printf("\n\n ... Press any key: ");
_getch();
return 0;
}
// Определения функций
double mean(int* arr, int N)
{
int i;
double aver = 0.0;
for (i = 0; i < N; i++)
aver += arr[i];
return (aver/N);
}
double median(int* arr, int N)
{
int i;
double med = 0.0;
for (i = 0; (i < N/2); i++)
med = arr[i];
if ( N % 2)
med = arr[i];
else
med = (med + arr[i])/2;
return med;
}
double mode(int* arr, int N)
{
int instances = 0,
tempinst = 1,
i = 1;
double tempmode,
mode_return = 1.0;
tempmode = (double)arr[0];
while (i < N)
{
while ((double)arr[i] == tempmode )
{
i++;
tempinst++;
}
if (tempinst > instances)
{
mode_return = tempmode;
instances = tempinst;
}
tempinst = 1;
tempmode = (double)arr[i];
i++;
}
return (mode_return);
}
В программе указатель на функции ( *fun ) – это массив указателей на функции, на три функции. В случае, когда возвращаемые значения функций имеют различный тип, то можно определить несколько указателей на функции.
Для понимания работы функции по расчету модуса алгоритм вычислений рекомендуется в [11.3] формулировать следующим образом: "Разбить сортированный список значений на ряд меньших списков, каждый из которых содержит одинаковые значения. Пересчитать число элементов в этих списках, и список с наибольшим числом элементов будет соответствовать модусу данных".
Результат выполнения программы показан на рис 11.4.
(рис 11.4) Значения среднего, медианы и модусаЗадание 3
mean(), median (), mode() в одну функцию и определите необходимый тип возвращаемого значения, чтобы в главной функции main() можно была распечатать результаты расчетов статистических характеристик.Пример 4. Напишите программу сортировки массива строк с использованием указателей на функции.
Программный код решения примера:
#include <stdio.h>
#include <conio.h>
#include <string.h>
// Прототипы функций
void bsort (char **arr, int size,
int (*comp) (const char *s1, const char *s2));
int less (const char *s1, const char *s2);
int greater (const char *s1, const char *s2);
int main (void)
{
char *Lines[] = { "asd", "aza", "baza", "qwerty", "hello", "world", "aza" };
int n = sizeof (Lines) / sizeof (Lines[0]);
int i;
// Вызов функции сортировки по возрастанию в алфавитном порядке
puts("\n The sorting in ascending order:");
bsort (Lines, n, less);
for (i = 0; i < n; ++i)
printf("\t %s\n", Lines[i]);
// Вызов функции сортировки по убыванию в алфавитном порядке
puts("\n The sorting in descending order:");
bsort (Lines, n, greater);
for (i = 0; i < n; ++i)
printf("\t %s\n", Lines[i]);
printf("\n\n ... Press any key: ");
_getch();
return 0;
}
// Определение функции сортировки строк
void bsort (char **arr, int size,
int (*comp) (const char *s1, const char *s2))
{
int i, j;
for (i = 0; i < size - 1; ++i)
for (j = 0; j < size - 1; ++j)
if (comp (arr[j], arr[j + 1]) > 0)
{
char *s = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = s;
}
}
// Определение функции сравнения строк по возрастанию
int less (const char *s1, const char *s2)
{
return strcmp (s1, s2);
}
// Определение функции сравнения строк по убыванию
int greater (const char *s1, const char *s2)
{
return -strcmp (s1, s2);
}
В программе используются указатель на функцию для вызова двух функций – less() и greater() в процессе сортировки для определения порядка расположения элементов (слов).
Результат выполнения программы показан на рис 11.5.
(рис 11.5) Пример сортировки строк Задание 4
Пример 5. Напишите программу построения на экране дисплея графика следующей функции:
$$y=sin(3x)e^{\frac x 3}.$$Предусмотрите возможность записи в текстовый файл графика данной функции.
Для решения примера используем средства вывода на печать форматированных данных без применения специальных графических библиотек.
Программный код решения примера:
/ Заголовочные файлы
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <math.h>
// Размеры диаграммы по ширине и высоте экрана
#define SCREENW 79
#define SCREENH 25
// Функция построения графика заданной функции
void plot (FILE *fout, double a, double b, double (*f) (double))
{
// Формальные параметры функции plot
// FILE *fout – указатель на поток вывода
// double a – левая граница оси абсцисс
// double b – правая граница оси абсцисс
// double (*f) (double) – указатель на функцию
char screen[SCREENW][SCREENH];
double x, y[SCREENW];
double ymin = 0, ymax = 0;
double hx, hy;
int i, j;
int xz, yz;
// hx – шаг по оси абсцисс
hx = (b - a) / (SCREENW - 1);
for (i = 0, x = a; i < SCREENW; ++i, x += hx) {
// вычисляем значение функции
y[i] = f (x);
// запоминаем минимальное и максимальное значения
if (y[i] < ymin) ymin = y[i];
if (y[i] > ymax) ymax = y[i];
}
hy = (ymax - ymin) / (SCREENH - 1);
yz = (int)floor (ymax / hy + 0.5);
xz = (int)floor (-a / hx + 0.5);
// рисование осей координат
for (j = 0; j < SCREENH; ++j) {
for (i = 0; i < SCREENW; ++i) {
if (j == yz i == xz)
screen[i][j] = '+'; // '.', '?', '+'
else if (j == yz)
screen[i][j] = '-';
else if (i == xz)
screen[i][j] = '|';
else
screen[i][j] = ' ';
}
}
// рисование графика функции
for (i = 0; i < SCREENW; ++i) {
j = (int)floor ((ymax - y[i]) / hy + 0.5);
screen[i][j] = '.'; // символ начертания графика
}
// вывод результата в файл или в стандартный поток stdout
for (j = 0; j < SCREENH; ++j) {
for (i = 0; i < SCREENW; ++i)
fputc (screen[i][j], fout);
fprintf (fout, "\n");
}
}
// Заданная функция
double f (double x)
{
return sin (3.0*x) * exp (-x / 3.0);
//return x * x - 3;
}
int main (void)
{
// Вывод графика в стандартный поток (консоль)
plot (stdout, 0.0, 10.0, f);
printf("\n\n … Press any key: ");
_getch();
return 0;
}
В программе используется указатель на файл, который может быть стандартным потоком, т. е. экран дисплея. В главной функции main() происходит обращение к функции рисования графика , в которую вводят фактические параметры, в частности файл – это stdout, т. е. стандартный поток, 0.0 – это левая граница оси абсцисс, 10.0 – правая граница оси абсцисс, f – имя функции с описанием зависимости y = f(x).
Пример выполнения программы показан на рис 11.6.
(рис 11.6) Пример построения графика функции на консолиЗадание 5
char?В предыдущей лабораторной работе рассматривались примеры функций, аргументами которых выступали указатели. В данной лабораторной работе подробнее будут рассмотрены вопросы, касающиеся указателей и функций.
Ранее было отмечено, что в языке С аргументы передаются в функции по значению и не существует прямого способа изменить переменную вызывающей функции, действуя внутри вызываемой функции. Благодаря аргументам-указателям функция может обращаться к объектам в вызвавшей ее функции, в том числе модифицировать их [11.1]. В качестве примера рассмотрим функцию swap(), в задачу которой входит обмен элементов местами. Для решения такой задачи необходимо передать из вызывающей программы (например, из главной функции main() ) в функцию указатели на переменные, которые нужно изменить. Программный код решения примера:
#include <stdio.h>
#include <conio.h>
// Прототип функции
void swap(int*, int*);
int main (void) {
int a = 10,
b = -20;
// Вывод на консоль исходных значений переменных
printf("\n Initial values:\n a = %d, b = %d\n", a, b);
// Вызов функции swap() с фактическими параметрами
swap(a, b);
// Результат после обращения функции swap()
printf("\n New values:\n a = %d, b = %d\n", a, b);
printf("\n ... Press any key: ");
_getch();
return 0;
}
// Определение функции
void swap(int *pa, int *pb)
{
int temp;
temp = *pa;
*pa = *pb;
*pb = temp;
}
В программе в качестве фактических параметров функции swap() выступают адреса заданных переменных. Можно было в главной функции определить указатели и инициализировать их адресами заданных переменных, а потом передать эти указатели в функцию swap.
Результат выполнения программы показан нa рис 11.1.
(рис 11.1) Результат обмена данными, выполненного функцией swap()Указатели, передаваемые в функцию, могут быть указателями на указатели. Указатели могут указывать на начало какого-либо массива и т. д. Указатели могут использоваться для защиты массивов, над которыми необходимо произвести некоторые вычисления или преобразования.
Особым свойством указателей можно считать возможность использовать их в качестве возвращаемых значений функций. Поскольку функции возвращают только одно значение, то несколько значений одного типа можно поместить в массив, а затем указатель на этот массив использовать в качестве возвращаемого значения.
Общая форма определения функции, которая возвращает указатель, следующая:
тип *имя_функции ( аргументы функции )
{
// тело функции
тип *имя_указателя;
?
return имя_указателя;
}
Рассмотрим пример, в котором осуществляется сложение двух одномерных массивов и результат возвращается через указатель.
Программный код решения примера:
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
int *out2(int A[], int B[], int);
int main (void) {
int i, n;
int A[] = {1,2,3,4,5};
int B[] = {2,2,2,2,2};
int *ptrAB = NULL;
n = (sizeof(A)/sizeof(A[0]));
puts("\n The initial arrays: ");
for (i = 0; i < n; i++)
printf(" %d", A[i]);
puts("");
for (i = 0; i < n; i++)
printf(" %d", B[i]);
ptrAB = out2(A, B, n);
puts("\n\n Result from function: ");
for (i = 0; i < n; i++)
printf(" %d", ptrAB[i]);
puts("\n\n Control of the arrays: ");
for (i = 0; i < n; i++)
printf(" %d", A[i]);
puts("");
for (i = 0; i < n; i++)
printf(" %d", B[i]);
free(ptrAB); // освобождение выделенной памяти
printf("\n\n ... Press any key: ");
_getch();
return 0;
}
int *out2(int A[], int B[], int n)
{
int i;
int *ptr = (int *)calloc(n, sizeof(int)); //выделение памяти
for (i = 0; i < n; i++)
ptr[i] = A[i] + B[i];
return ptr;
}
Программа не требует особых пояснений.
Следует отметить, что никогда не следует возвращать
Указатели возвращаются подобно значениям любых других типов данных. Чтобы вернуть указатель, функция должна объявить его тип в качестве типа возвращаемого значения. Таким образом, если функция возвращает указатель, то значение, используемое в ее инструкции return, также должно быть указателем. В частности, многие библиотечные функции, предназначенные для обработки строк, возвращают указатели на символы.
В языке С существует такой механизм как указатель на функцию. Допустим, существует несколько функций для различных операций с данными. В этом случае оказывается удобным определить указатель на функцию, и использовать его там, где требуется производить расчет для различных функций.
Указатель на функцию – это переменная, содержащая адрес в памяти, по которому расположена функция [11.2]. Имя функции – это адрес начала программного кода функции. Указатели на функции могут быть переданы функциям в качестве аргументов, могут возвращаться функциями, сохраняться в массивах и присваиваться другим указателям на функции [11.2].
Типичное определение указателя на функцию следующее:
тип_возвращаемый_функцией(*имя_указателя_на_функцию)(аргументы);
В приведенном объявлении используются круглые скобки, в которых собственно и определяется указатель на функцию, которая возвращает тот или иной тип – тип_возвращаемый_функцией. Хотя знак * обозначает *имя_указателя_на_функцию. Очевидно, что возможны сложные объявлений функций.
Указатели на функции часто используются в системах, управляемых меню [11.2]. Пользователь выбирает команду меню (одну из нескольких). Каждая команда обслуживается своей функцией. Указатели на каждую функцию находятся в массиве указателей. Выбор пользователя служит индексом, по которому из массива выбирается указатель на нужную функцию.
Другим типичным применением указателей на функции являются реализация обобщенных алгоритмов, например, алгоритмов сортировки и поиска. В этом случае критерии сортировки и поиска реализуются в виде отдельных функций и передаются при помощи указателей на функции в качестве параметра реализации основного алгоритма.
Пример 1. Напишите программу с функцией
В условии примера "вызов по ссылке" означает, что в качестве фактических параметров функций будут использоваться
Программный код решения примера:
#include <stdio.h>
#include <conio.h>
// Прототип функции
void bsort (int* const, const int);
int main (void)
{
int A[] = {56, 34, 2, 0, 1, -21, 6, 8, 7};
int i, n;
//Размерность массива
n = sizeof(A)/sizeof(A[0]);
puts("\n Data items in original order:");
for (i = 0; i < n; i++)
printf(" %3d", A[i]);
// Вызов функции сортировки - bsort()
bsort (A, n);
puts("\n\n Data items in ascending order:");
for (i = 0; i < n; i++)
printf(" %3d", A[i]);
printf("\n\n ... Press any key: ");
_getch();
return 0;
}
// Определение функции
void swap(int *pa, int *pb)
{
int temp;
temp = *pa;
*pa = *pb;
*pb = temp;
}
void bsort (int *const arr, const int size)
{
int pass, //счетчик проходов
j; // счетчик сравнений
// Прототип функции обмена - swap()
void swap (int*, int*);
// Цикл для контроля проходов
for (pass = 0; pass < size - 1; pass++ )
{
// цикл для контроля сравнений на данном проходе
for (j = 0; j < size - 1; j++)
{
// обмен значений при нарушении порядка возрастания
if (arr[j] > arr[j + 1])
{
swap(arr[j], arr[j+1]);
}
}
}
}
В программе функция сортировки bsort() в качестве формального параметра используется константный указатель, который указывает на первый элемент заданного массива. Второй формальный параметр также константный, чтобы подчеркнуть неизменность этого параметра в теле функции bsort(). Передача функции размера массива в качестве параметра имеет два преимущества – это хороший стиль программирования и, кроме того, такую функцию можно использовать многократно.
Прототип функции swap() включен в тело функции bsort(), потому что это единственная функция, которая вызывает функцию обмена swap().
Пример выполнения программы показан на рис 11.2.
(рис 11.2) Пример сортировки массива методом пузырькаЗадание 1
bsort() возвращала указатель на отсортированный массив, а сам исходный массив был при этом неизменным. Предусмотрите вывод на консоль исходного массива, потом отсортированного массива после вызова функции сортировки, и снова для контроля исходный массив. При этом аргументы функции bsort() оставить без изменения.Пример 2. Напишите программу, в которой используется функция по расчету среднего значения (среднего арифметического) одномерного числового массива, его исправленной выборочной дисперсии и среднего квадратичного отклонения (main()
Приведем формулы, по которым рассчитываются среднее значение выборки, исправленная выборочная дисперсия и
Среднее выборочное значение одномерного массива размерностью N
$$mean=\frac 1 N \sum^N_{i=1}ai,$$где – элементы массива.
Исправленная выборочная дисперсия одномерного массива размерностью N
$$D=\frac 1 {N-1} \sum^N_{i=1}(a_i-mean)^2,$$где mean – среднее значение данного массива
Исправленное
где D – дисперсия данного массива.
Программный код решения примера:
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <math.h>
// Прототип функции
double *mean_D_S(int arr[], int n);
int main(void)
{
int mass[] = {2, -3, 5, 6, 7, 8, 9,-1};
int i, n;
double *R;
n = sizeof(mass)/sizeof(mass[0]);
puts("\n\t Initial array:");
for (i = 0; i < n; i++)
printf(" %3d", mass[i]);
// Вызов функции
R = mean_D_S(mass, n);
// Вывод расчтных характеристик массива
printf("\n\n The average value of an array: %g\n", R[0]);
printf(" The dispersion of the array: %g\n", R[1]);
printf(" The standard deviation of the array: %g\n", R[2]);
// Освобождение памяти
free(R);
printf("\n ... Press any key: ");
_getch();
return 0;
}
// Определение функции
double *mean_D_S(int arr[], int N)
{
int j;
double *PTR3 = (double *)calloc(N, sizeof(double));
double mean, D, S;
mean = 0.0;
for (j = 0; j < N; j++)
mean += arr[j];
mean /= N;
D = 0.0;
for (j = 0; j < N; j++)
D += (arr[j] - mean)*(arr[j] - mean);
D /= (N-1);
S = sqrt(D);
PTR3[0] = mean;
PTR3[1] = D;
PTR3[2] = S;
return PTR3;
}
В программе используется функция calloc(), которая выделенные ячейки памяти обнуляет. Расчетные характеристики одномерного массива размещаются последовательно друг за другом в выделенной памяти для указателя *PTR3. Сформированный указатель функция возвращает в точку вызова функции mean_D_S().
Результат выполнения программы показан на рис 11.3.
(рис 11.3) Расчет статистических характеристик числового массиваЗадание 2
mean_D_S() используйте указатель на числовой массив.mean_D_S() еще исходного массива, поэлементно возведенного в квадрат. В главной функции main() результаты выведите на консоль.float.Пример 3. Напишите программу с указателем на функции, которые рассчитывают следующие статистические характеристики одномерного числового массива: среднее арифметическое значение, медиану и модус (моду).
Среднее арифметическое рассчитывалось в предыдущем примере.
Приведем определения медианы и модуса, взятые из книги [11.3], из которой также взяты основные фрагменты программных кодов (в книге программы написаны на С++).
Медиана – это серединное значение в наборе данных – т. е. такое, что ровно половина значений располагается выше, и ровно половина ниже его.
Модус – это значение, наиболее часто встречающееся в наборе данных.
Программный код решения примера:
#include <stdio.h>
#include <conio.h>
double mean(int*, int);
double median(int*, int);
double mode(int*, int);
int main(void) {
int mass[] = {5, 6, 5, 5, 3,
6, 5, 3, 1, 4,
5, 3, 1, 6, 5,
2, 5, 2, 3, 4};
int *ptr = mass;
int temp, i, j, k, n;
// Указатель на функции
double (*fun[3])(int*, int) = {mean, median, mode};
n = sizeof(mass)/sizeof(mass[0]);
puts("\n The original array:");
j = 0;
for (i = 0; i < n; i++) {
j++;
if ( j%6 )
printf(" %2d", mass[i]);
else
{puts(""); printf(" %2d", mass[i]); j = 1;}
}
// Сортировка методом выбора
for (i = 0; i < (n - 1); ++i) {
temp = ptr[i]; k = i;
for (j = i + 1; j < n; ++j)
if (ptr[j] < temp) { k = j; temp = ptr[k]; }
ptr[k] = ptr[i]; ptr[i] = temp;
}
puts("\n\n Results - mean, mediana, modus: ");
for (i = 0; i < 3; i++)
printf("%6g\n",(*fun[i])(ptr, n));
printf("\n\n ... Press any key: ");
_getch();
return 0;
}
// Определения функций
double mean(int* arr, int N)
{
int i;
double aver = 0.0;
for (i = 0; i < N; i++)
aver += arr[i];
return (aver/N);
}
double median(int* arr, int N)
{
int i;
double med = 0.0;
for (i = 0; (i < N/2); i++)
med = arr[i];
if ( N % 2)
med = arr[i];
else
med = (med + arr[i])/2;
return med;
}
double mode(int* arr, int N)
{
int instances = 0,
tempinst = 1,
i = 1;
double tempmode,
mode_return = 1.0;
tempmode = (double)arr[0];
while (i < N)
{
while ((double)arr[i] == tempmode )
{
i++;
tempinst++;
}
if (tempinst > instances)
{
mode_return = tempmode;
instances = tempinst;
}
tempinst = 1;
tempmode = (double)arr[i];
i++;
}
return (mode_return);
}
В программе указатель на функции ( *fun ) – это массив указателей на функции, на три функции. В случае, когда возвращаемые значения функций имеют различный тип, то можно определить несколько указателей на функции.
Для понимания работы функции по расчету модуса алгоритм вычислений рекомендуется в [11.3] формулировать следующим образом: "Разбить сортированный список значений на ряд меньших списков, каждый из которых содержит одинаковые значения. Пересчитать число элементов в этих списках, и список с наибольшим числом элементов будет соответствовать модусу данных".
Результат выполнения программы показан на рис 11.4.
(рис 11.4) Значения среднего, медианы и модусаЗадание 3
mean(), median (), mode() в одну функцию и определите необходимый тип возвращаемого значения, чтобы в главной функции main() можно была распечатать результаты расчетов статистических характеристик.Пример 4. Напишите программу сортировки массива строк с использованием указателей на функции.
Программный код решения примера:
#include <stdio.h>
#include <conio.h>
#include <string.h>
// Прототипы функций
void bsort (char **arr, int size,
int (*comp) (const char *s1, const char *s2));
int less (const char *s1, const char *s2);
int greater (const char *s1, const char *s2);
int main (void)
{
char *Lines[] = { "asd", "aza", "baza", "qwerty", "hello", "world", "aza" };
int n = sizeof (Lines) / sizeof (Lines[0]);
int i;
// Вызов функции сортировки по возрастанию в алфавитном порядке
puts("\n The sorting in ascending order:");
bsort (Lines, n, less);
for (i = 0; i < n; ++i)
printf("\t %s\n", Lines[i]);
// Вызов функции сортировки по убыванию в алфавитном порядке
puts("\n The sorting in descending order:");
bsort (Lines, n, greater);
for (i = 0; i < n; ++i)
printf("\t %s\n", Lines[i]);
printf("\n\n ... Press any key: ");
_getch();
return 0;
}
// Определение функции сортировки строк
void bsort (char **arr, int size,
int (*comp) (const char *s1, const char *s2))
{
int i, j;
for (i = 0; i < size - 1; ++i)
for (j = 0; j < size - 1; ++j)
if (comp (arr[j], arr[j + 1]) > 0)
{
char *s = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = s;
}
}
// Определение функции сравнения строк по возрастанию
int less (const char *s1, const char *s2)
{
return strcmp (s1, s2);
}
// Определение функции сравнения строк по убыванию
int greater (const char *s1, const char *s2)
{
return -strcmp (s1, s2);
}
В программе используются указатель на функцию для вызова двух функций – less() и greater() в процессе сортировки для определения порядка расположения элементов (слов).
Результат выполнения программы показан на рис 11.5.
(рис 11.5) Пример сортировки строкЗадание 4
Пример 5. Напишите программу построения на экране дисплея графика следующей функции:
$$y=sin(3x)e^{\frac x 3}.$$Предусмотрите возможность записи в текстовый файл графика данной функции.
Для решения примера используем средства вывода на печать форматированных данных без применения специальных графических библиотек.
Программный код решения примера:
/ Заголовочные файлы
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <math.h>
// Размеры диаграммы по ширине и высоте экрана
#define SCREENW 79
#define SCREENH 25
// Функция построения графика заданной функции
void plot (FILE *fout, double a, double b, double (*f) (double))
{
// Формальные параметры функции plot
// FILE *fout – указатель на поток вывода
// double a – левая граница оси абсцисс
// double b – правая граница оси абсцисс
// double (*f) (double) – указатель на функцию
char screen[SCREENW][SCREENH];
double x, y[SCREENW];
double ymin = 0, ymax = 0;
double hx, hy;
int i, j;
int xz, yz;
// hx – шаг по оси абсцисс
hx = (b - a) / (SCREENW - 1);
for (i = 0, x = a; i < SCREENW; ++i, x += hx) {
// вычисляем значение функции
y[i] = f (x);
// запоминаем минимальное и максимальное значения
if (y[i] < ymin) ymin = y[i];
if (y[i] > ymax) ymax = y[i];
}
hy = (ymax - ymin) / (SCREENH - 1);
yz = (int)floor (ymax / hy + 0.5);
xz = (int)floor (-a / hx + 0.5);
// рисование осей координат
for (j = 0; j < SCREENH; ++j) {
for (i = 0; i < SCREENW; ++i) {
if (j == yz i == xz)
screen[i][j] = '+'; // '.', '?', '+'
else if (j == yz)
screen[i][j] = '-';
else if (i == xz)
screen[i][j] = '|';
else
screen[i][j] = ' ';
}
}
// рисование графика функции
for (i = 0; i < SCREENW; ++i) {
j = (int)floor ((ymax - y[i]) / hy + 0.5);
screen[i][j] = '.'; // символ начертания графика
}
// вывод результата в файл или в стандартный поток stdout
for (j = 0; j < SCREENH; ++j) {
for (i = 0; i < SCREENW; ++i)
fputc (screen[i][j], fout);
fprintf (fout, "\n");
}
}
// Заданная функция
double f (double x)
{
return sin (3.0*x) * exp (-x / 3.0);
//return x * x - 3;
}
int main (void)
{
// Вывод графика в стандартный поток (консоль)
plot (stdout, 0.0, 10.0, f);
printf("\n\n … Press any key: ");
_getch();
return 0;
}
В программе используется указатель на файл, который может быть стандартным потоком, т. е. экран дисплея. В главной функции main() происходит обращение к функции рисования графика , в которую вводят фактические параметры, в частности файл – это stdout, т. е. стандартный поток, 0.0 – это левая граница оси абсцисс, 10.0 – правая граница оси абсцисс, f – имя функции с описанием зависимости y = f(x).
Пример выполнения программы показан на рис 11.6.
(рис 11.6) Пример построения графика функции на консолиЗадание 5
char?Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.