- •Предисловие
- •Контрольные вопросы
- •Библиографический список
- •Тема 2 Переменные и базовые типы данных языка с
- •Контрольные вопросы
- •Библиографический список
- •Тема3 Организация циклов в языке с
- •Контрольные вопросы
- •Библиографический список
- •Тема 4 Принятие решений. Условные операторы в языке с
- •Контрольные вопросы
- •Библиографический список
- •Тема 5 Числовые массивы в языке программирования с
- •Тип имя_массива[размер];
- •Тип имя_массива[размер1] [размер2];
- •Тип имя_массива[размер1] [размер2] [размерN];
- •Контрольные вопросы
- •Библиографический список
- •Тема 6 Символьные массивы в языке с. Работа со строками
- •Тип имя_массива[размер];
- •Тип имя_массива[размер1] [размер2];
- •Тип имя_массива[размер1] [размер2] [размерN];
- •Контрольные вопросы
- •Библиографический список
- •Тема 7 Указатели в языке программирования с
- •Int *ptr; // объявили указатель на целую переменную
- •Контрольные вопросы
- •Библиографический список
- •Тема 8 Указатели и массивы в языке с
- •Int data[7]; // обычный массив
- •Int *pd[7]; // массив указателей
- •Контрольные вопросы
- •Библиографический список
- •Тема 9 Динамическое распределение памяти в языке с
- •If (!ptr) // условие логического отрицания
- •If (!ptr) // условие логического отрицания
- •Контрольные вопросы
- •Библиографический список
- •Тема 10 Функции Общие сведения о функциях языка с
- •Fun(тип имя_перем1, тип имя_перем2, , тип имя_перем n)
- •Контрольные вопросы
- •Библиографический список
- •Тема 11 Указатели и функции в языке программирования с
- •Тип_возвращаемый_функцией(*имя_указателя_на_функцию)(аргументы);
- •Контрольные вопросы
- •Библиографический список
- •Тема 12 Файловый ввод/вывод в языке с
- •Контрольные вопросы
- •Библиографический список
- •Тема 13 Структуры – производные типы данных языка с
- •Int age; // возраст
- •Int age; // возраст
- •Int age; // возраст
- •Int age; // возраст
- •Int age; // возраст
- •Int age; // возраст
- •Контрольные вопросы
- •Библиографический список
- •Тема 14 Объединения и перечислимые типы в языке с
- •Контрольные вопросы
- •Библиографический список
- •Тема 15 Структуры и функции языка с
- •Контрольные вопросы
- •Библиографический список
- •Тема 16 Операции с разрядами (битами) в языке с
- •Контрольные вопросы
- •Библиографический список
- •Тема 17 Программы, состоящие из нескольких файлов, на языке с
- •Спецификатор extern
- •Спецификатор static
- •Спецификатор register
- •Спецификатор auto
- •Контрольные вопросы
- •Библиографический список
- •Тема 18 Рекурсивные алгоритмы и функции
- •Переместить (a, b);
- •Контрольные вопросы
- •Библиографический список
- •Тема 19 Препроцессор языка с
- •Директива #define
- •Директива #error
- •Директива #include
- •Директивы условной компиляции
- •2_ Я_последовательность операторов программного кода
- •3_ Я_последовательность операторов программного кода
- •Директива #line
- •Директива #pragma
- •Предопределенные символические константы
- •Макрос подтверждения assert
- •Контрольные вопросы
- •Библиографический список
- •Тема 20 Программы на языке с при использовании статически подключаемой библиотеки
- •Контрольные вопросы
- •Библиографический список
- •Тема 21 Использование аргументов командной строки в с
- •Контрольные вопросы
- •Контрольная работа № 2 Покупки в супермаркете
- •Приложение Управление конфигурациями проекта в Visual Studio 2010
Тип_возвращаемый_функцией(*имя_указателя_на_функцию)(аргументы);
В круглых скобках определяется указатель на функцию, которая возвращает тот или иной тип – тип_возвращаемый_функцией. Хотя знак * обозначает префиксную операцию, он имеет более низкий приоритет, чем функциональные круглые функции, поэтому для правильного комбинирования частей объявления необходимы дополнительные скобки [1]. При этом аргументы – это аргументы той или иной функции с заданным типом возвращаемого значения, для которой определяется указатель *имя_указателя-_на_функцию. Очевидно, что возможны сложные объявления функций.
Указатели на функции часто используются в системах, управляемых меню [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().
Приведем расчетные формулы.
Среднее выборочное значение (mean) одномерного массива размерностью N
где – элементы массива.
Исправленная выборочная дисперсия (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 D, S, mean; mean = D = 0.0;
for (j = 0; j < N; j++) mean += arr[j];
mean /= N; // среднее арифметическое
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; }
|
Расчетные характеристики одномерного массива размещаются последовательно друг за другом в выделенной памяти для указателя *PTR3. Сформированный указатель функция возвращает в точку вызова функции mean_D_S().
Р езультат выполнения программы показан на рис. 11.3.
Рис. 11.3. Расчет статистических характеристик числового массива
Задание 2
В качестве первого аргумента функции mean_D_S() используйте указатель на числовой массив.
Воспользуйтесь справкой по математическим функциям и в программе примените функцию, которая осуществляет возведение в степень.
Дополните возврат функцией mean_D_S() исходного массива, поэлементно возведенного в квадрат. В главной функции main() результаты выведите на консоль.
Пример 3. Написать программу с указателем на функции, которые рассчитывают следующие статистические характеристики одномерного числового массива: среднее арифметическое значение, медиану и модус (моду).
Среднее арифметическое рассчитывалось в предыдущем примере.
Приведем определения медианы и модуса [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 temp, i, j, k, n, *ptr = mass; // Указатель на функции 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) – это массив указателей на три функции. В случае когда возвращаемые значения функций имеют различный тип, можно определить несколько указателей на функции.
Для понимания работы функции по расчету модуса алгоритм вычислений рекомендуется формулировать следующим образом: «Разбить сортированный список значений на ряд меньших списков, каждый из которых содержит одинаковые значения. Пересчитать число элементов в этих списках, и список с наибольшим числом элементов будет соответствовать модусу данных» [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 i; int n = sizeof (Lines) / sizeof (Lines[0]); // Вызов функции сортировки по возрастанию в алфавитном порядке 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. Написать программу построения на экране дисплея графика функции
Предусмотреть возможность записи в текстовый файл графика данной функции.
Для решения примера используем средства вывода на печать форматированных данных без применения специальных графических библиотек.
Программный код решения примера
#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 hx, hy, ymin = 0, ymax = 0; int i, j, 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() происходит обращение к функции рисования графика plot(), в которую вводят фактические параметры, в частности файл – это stdout, т. е. стандартный поток, 0.0 – это левая граница оси абсцисс, 10.0 – правая граница оси абсцисс, f – имя функции с описанием зависимости y = f(x).
Пример выполнения программы представлен на рис. 11.6.
Р ис. 11.6. Пример построения графика функции на консоли
Задание 5
Выполните вывод графика в текстовый файл с именем compX.txt, где Х – номер компьютера, на котором выполняется лабораторная работа.
Проанализируйте программу с целью возможного улучшения вида графика заданной функции.
В программу добавьте описание кубической и параболической функций. Напишите необходимые строчки программного кода для запроса о построении графика соответствующей функции.