
- •1 Основні елементи мови с
- •1.1 Алфавіт мови програмування
- •1.2 Лексеми
- •1.3 Ключові слова
- •1.4 Ідентифікатори
- •1.5 Класифікація типів даних
- •1.6 Літерали
- •1.7 Оператори
- •1.8 Коментарі
- •1.9 Директиви препроцесора
- •1.10 Організація програми
- •2 Операції та вирази
- •2.1 Загальні відомості
- •2.2 Арифметичні операції
- •2.3 Операції приведення типів
- •2.4 Операції присвоєння
- •2.5 Операції інкремента і декремента
- •2.6 Операції порівняння
- •2.7 Операції зсуву
- •2.8 Порозрядні операції
- •2.9 Логічні операції
- •2.10 Операція sizeof
- •2.11 Операція послідовного обчислення
- •2.12 Операція умови (?:)
- •2.13 Адресні операції
- •3 Прості типи даних
- •3.1 Оголошення змінних
- •3.2 Час існування та область видимості змінних
- •3.3 Цілі типи даних
- •3.4 Дійсні типи даних
- •4 Оператори керування
- •4.1 Оператор розгалуження if
- •4.2 Оператор розгалуження if-else
- •4.3 Оператор множинного розгалуження switch
- •4.4 Оператор циклу for
- •4.5 Оператор циклу while
- •4.6 Оператор циклу do while
- •4.7 Оператор break
- •4.8 Оператор continue
- •5 Функції
- •5.1 Основні поняття
- •5.2 Види виклику функцій
- •5.3 Область видимості
- •5.4 Порожній тип void
- •5.5 Передача аргументів у функцію
- •5.6 Рекурсивні функції
- •5.7 Прототипи функцій
- •6 Покажчики
- •6.1 Визначення та ініціалізація покажчиків
- •6.2 Визначення покажчиків
- •6.3 Масиви
- •6.4 Операції порівняння
- •6.5 Копіювання рядка
- •6.6 Покажчики на функцію
- •6.7 Покажчики на void
- •6.8 Арифметика покажчиків
- •7 Масиви
- •7.1 Загальні поняття
- •7.2 Одновимірні масиви
- •7.3 Багатовимірні масиви
- •8 Рядки в с
- •8.1 Рядки
- •8.2.Створення рядків
- •8.3 Прототипи
- •8.3 Функції перетворення буферів
- •8.4 Функції перевірки літер
- •8.5 Операції з рядками
- •9 Структури, об’єднання, перерахування
- •9.1 Структури
- •9.2 Бітові поля
- •9.3 Ключове слово typedef
- •9.4 Об’єднання
- •9.5 Перераховуваний тип
- •10 Введення та виведення даних
- •10.1 Функція виведення printf
- •10.2Функція введення scanf
- •10.3 Введення та виведення у файл
- •11 Динамічне виділення пам'яті
6.6 Покажчики на функцію
Покажчики на функцію ― дуже потужний засіб мови С. Хоча не можна не відзначити, що це дуже важкий для розуміння термін. Функція розташовується в пам'яті за певною адресою, який можна присвоїти покажчику в якості його значення. Адресою функції є її точка входу. Саме ця адреса використовується при виклику функції. Так як покажчик зберігає адресу функції, то вона може бути викликана за допомогою цього покажчика. Він дозволяє також передавати її іншим функціям як аргумент.
У програмі на мові С адресою функції служить її ім'я без дужок і аргументів (це схоже на адресу масиву, який дорівнює імені масиву без індексів). Розглянемо наступну програму, в якій порівнюються два рядки, введені користувачем. Зверніть увагу на оголошення функції check () і покажчик p всередині main (). Покажчик p є покажчиком на функцію.
Приклад 6.16. Порівняння стрічок з використанням покажчиків на функцію.
# include <stdio.h>
# include <string.h>
void check (char *a, char * b,
int (*cmp) (const char *, const char *));
int main (void)
{
char s1 [80], s2 [80];
int (* p) (const char *, const char *);
/* Покажчик на функцію */
p = strcm /* Привласнює адресу функції strcmp
вказівником p* */
printf ("Enter two strings.\ n");
gets (s1);
gets (s2);
check (s1, s2, p); /* Передає адресу функції strcmp
за допомогою покажчика p */
return 0;
}
void check (char * a, char * b,
int (* cmp) (const char *, const char *))
{
printf ("Check for overlap \ n");
if (! (* cmp) (a, b)) printf ("The same. ");
else printf ("Not the same. ");
}
Проаналізуємо цю програму докладно. У першу чергу розглянемо оголошення покажчика p в main ():
int (* p) (const char *, const char *);
Це оголошення повідомляє компілятору, що p – це покажчик на функцію, що має два параметри типу const char * і повертає значення типу int. Дужки навколо p необхідні для правильної інтерпретації оголошення компілятором. Подібна форма оголошення використовується також для покажчиків на будь-які інші функції, потрібно лише внести зміни в залежності від типу, що повертається і параметрів функції.
Тепер розглянемо функцію check ().У ній оголошені три параметри: два покажчика на символьний тип (a і b) і покажчик на функцію cmp. Зверніть увагу на те, що покажчик функціїcmp оголошено в тому ж форматі, що і p. Тому в cmp можна зберігати значення покажчика на функцію, що має два параметри типу const char * і повертає значення int. Як і в оголошенні p, круглі дужки навколо *cmp необхідні для правильної інтерпретації цього оголошення компілятором.
Спочатку в програмі покажчиком p присвоюється адресу стандартної бібліотечної функції strcmp (), яка порівнює рядки. Потім програма просить користувача ввести два рядки і передає покажчики на них функції check (), яка їх порівнює. Усередині check () вираз (*Cmp) (a, b) викликає функцію strcmp (), на яку вказує cmp, з аргументами a і b. Дужки навколо *cmp обов'язкові. Існує й інший, більш простий, спосіб виклику функції за допомогою покажчика:
cmp (a, b);
Однак перший спосіб використовується частіше (рекомендується використовувати саме його), тому що при другому способі виклику покажчик cmp дуже схожий на ім'я функції, що може збити з пантелику читає програму. У той же час у першого способу запису є свої переваги, наприклад, добре видно, що функція викликається за допомогою покажчика на функцію, а не імені функції. Слід зазначити, що спочатку в С був визначений саме перший спосіб виклику.
Виклик функції check () можна записати, використовуючи безпосередньо ім'я strcmp ():
check (s1, s2, strcmp);
В цьому випадку вводити в програму додатковий покажчик p немає необхідності.
Виникає запитання: яка користь від виклику функції за допомогою покажчика на функцію? Адже в даному випадку ніяких переваг не досягнуто, цим ми тільки ускладнили програму. Тим не менш, у багатьох випадках виявляється більш вигідним передати ім'я функції як параметр або навіть створити масив функцій. Наприклад, у програмі інтерпретатора синтаксичний аналізатор (програма, що аналізує висловлювання) часто викликає різні допоміжні функції, такі як обчислення математичних функцій, процедури введення-виведення і т.п. У таких випадках найчастіше створюють список функцій і викликають їх за допомогою індексів.
Альтернативний підхід – використання оператора switch з довгим списком міток case – робить програму більш громіздкою і схильною до помилок.
У наступному прикладі розглядається розширена версія попередньої програми. У цій версії функція check () влаштована так, що може виконувати різні операції над рядками s1 і s2 (наприклад, порівнювати кожен символ з відповідним символом іншого рядка або порівнювати числа, записані в рядках) залежно від того, яка функція вказана в списку аргументів .Наприклад, рядки "0123" і "123" відрізняються, проте представляють один і той же числове значення.
Приклад 6.17. Детальна версія попередньої програми (функція check () влаштована так, що може виконувати різні операції над рядками s1 і s2)
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
void check (char*a, char*b,
int (*cmp)(const char*, const char*));
int compvalues(const char *a, const char*b);
int main(void)
{
char s1[80], s2[80];
printf("Enter two strings.\n");
gets(s1);
gets(s2);
if (isdigit(*s1)) {
printf(".\n");
check(s1, s2, compvalues);
}
else {
printf("Check for overlap.\n");
check(s1, s2, strcmp);
}
return 0;
}
void check(char*a, char*b,
int (*cmp)(const char*, const char*))
{
if(!(*cmp)(a, b)) printf("The same ");
else printf("Not the same ");
}
int compvalues(const char *a, const char *b)
{
if(atoi(a)==atoi(b)) return 0;
else return 1;
}
Результат роботи програми:
Якщо в цьому прикладі ввести перший символ першого рядка як цифру, то check () використовує compvalues (), в іншому випадку – strcmp (). Функція check () викликає ту функцію, ім'я якої зазначено в списку аргументів при виклику check (), тому вона в різних ситуаціях може викликати різні функції.