- •Введение
- •1. ТИПЫ ДАННЫХ И ОПЕРАТОРЫ
- •1.1. Переменные и базовые типы данных
- •1.2. Операции и выражения
- •1.3. Символические константы
- •1.5. Несколько слов о функции main()
- •2. ВВОД И ВЫВОД В СИ
- •2.2. Форматный ввод-вывод
- •3. ЦИКЛЫ И ОПЕРАТОРЫ СРАВНЕНИЯ
- •3.1. Условный оператор
- •3.2. Оператор выбора switch
- •3.3. Операторы цикла
- •3.4. Операторы break и continue
- •3.5. Примеры
- •3.6. Вычисление значений элементарных функций
- •3.7. Задачи
- •4. ОБРАБОТКА ПОСЛЕДОВАТЕЛЬНОСТЕЙ
- •4.1. Примеры
- •4.2. Задачи
- •5. ОДНОМЕРНЫЕ МАССИВЫ
- •5.1. Начальные сведения о массивах
- •5.2. Примеры работы с массивами
- •5.3. Задачи
- •6. МНОГОМЕРНЫЕ МАССИВЫ
- •6.1. Определение и инициализация двумерных массивов
- •6.2. Примеры с двумерными массивами
- •6.3. Задачи
- •7. УКАЗАТЕЛИ И МАССИВЫ
- •7.1. Указатели и адреса
- •7.2. Указатели и аргументы функций
- •7.3. Указатели и массивы
- •7.4. Операции с указателями
- •7.5. Указатели с типом void
- •7.6. Модификатор const
- •7.7. Массивы переменного размера
- •7.8. Массивы указателей
- •7.9. Двумерные массивы переменного размера
- •8. СИМВОЛЫ И СТРОКИ
- •8.1. Представление символьной информации в ЭВМ
- •8.2. Библиотека обработки символов
- •8.3. Строки в языке Си
- •8.4. Функции обработки строк
- •8.5. Функции преобразования строк
- •8.6. Примеры работы со строками
- •8.7. Разбиение строки на лексемы
- •8.8. Задачи
- •9. СТРУКТУРЫ
- •9.1. Основные сведения о структурах
- •9.2. Объединения
- •10. ДИРЕКТИВЫ ПРЕПРОЦЕССОРА
- •10.1. Директива #include
- •10.2. Директива #define
- •10.3. Директива #undef
- •10.4. Условная компиляция
- •11. ФУНКЦИИ
- •11.1. Основные сведения о функциях
- •11.2. Прототипы функций
- •11.3. Классы памяти
- •11.4. Указатели на функции
- •11.5. Рекурсия
- •11.6. Примеры с использованием рекурсии
- •11.7. Метод «разделяй и властвуй»
- •11.8. Задачи на применение рекурсии
- •12. РАБОТА С БИТАМИ ПАМЯТИ
- •12.1. Битовые операции
- •12.2. Примеры с использованием битовых операций
- •12.3. Задачи
- •13. РАБОТА С ФАЙЛАМИ
- •13.1. Файлы и потоки
- •13.2. Текстовые файлы
- •13.3. Двоичные файлы
- •13.4. Шифрование файлов
- •13.5. Задачи на текстовые файлы
- •13.6. Задачи на двоичные файлы
- •14. СТРУКТУРЫ ДАННЫХ
- •14.1. Односвязные списки
- •14.2. Примеры работы с односвязными списками
- •14.3. Задачи на односвязные списки
- •14.4. Стеки, очереди
- •14.5. Задачи на стеки и очереди
- •14.6. Двусвязные списки
- •14.7. Задачи на двусвязные списки
- •14.8. Бинарные деревья
- •14.9. Примеры с использованием бинарных деревьев
- •14.10. Задачи на бинарные деревья
- •Приложение 1. АЛГОРИТМЫ ПОИСКА
- •1. Линейный поиск
- •2. Поиск с барьером
- •3. Двоичный поиск
- •Приложение 2. АЛГОРИТМЫ СОРТИРОВКИ
- •Несколько слов о сложности алгоритмов
- •1. Метод прямого выбора
- •2. Метод прямого включения
- •3. Пузырьковая сортировка
- •4. Шейкерная сортировка
- •5. Быстрая сортировка
- •6. Сортировка подсчетом
- •Приложение 3. СОРТИРОВКА ИНДЕКСОВ И УКАЗАТЕЛЕЙ
- •1. Сортировка индексов на основе метода прямого выбора
- •2. Сортировка индексов на основе пузырьковой сортировки
- •3. Сортировка индексов на основе быстрой сортировки
- •4. Сортировка двумерных массивов
- •5. Сортировка строк
- •Приложение 4. СОРТИРОВКА ФАЙЛОВ И СПИСКОВ
- •1. Сортировка двоичных файлов
- •2. Сортировка линейных списков
- •Приложение 5. СОРТИРОВКА С УСЛОВИЕМ
- •1. Сортировка с условием на базе пузырьковой сортировки
- •2. Сортировка с условием на базе быстрой сортировки
- •3. Сортировка с условием двоичных файлов
- •4. Сортировка с условием линейного списка на базе пузырьковой сортировки
- •5. Сортировка с условием линейного списка на базе быстрой сортировки
- •ЛИТЕРАТУРА
}
int main( )
{
int j = 1;
for (j = 1; j <= 10; j++) Print();
return 0;
}
11.4. Указатели на функции
Функции, как и любые элементы данных, имеют адреса. Как и имя массива является адресом первого элемента массива, так и имя функции является адресом начала машинного кода данной функции. Поэтому можно определить указатель на функцию и работать с ним как с обычной переменной. Указатели на функции можно передавать другим функциям в качестве аргументов, можно возвращать в качестве результатов работы функций, можно размещать в массивах и т.д.
Чтобы получить адрес той или иной функции, достаточно записать ее имя (которое и содержит ее адрес) без замыкающих скобок, то есть если F() – некоторая функция, то F – адрес данной функции. Например:
#include<stdio.h>
int Compare(int a, int b)
{
return a >= b;
}
int main( )
{
printf("%p\n", Compare); printf("%p\n", main); getch();
return 0;
}
175
Поэтому чтобы передать некоторой функции адрес той или иной функции, достаточно передать имя передаваемой функции.
Следующий пример иллюстрирует применение указателя на функцию.
#include<stdio.h> |
|
int ShowMsg(char *msg) |
|
{ |
|
return puts(msg); |
|
} |
|
int main( ) |
|
{ |
|
int (*pFunc)(char *); |
/* указатель на функцию */ |
pFunc = ShowMsg; |
|
(*pFunc)("A message \n"); |
/* вызываем функцию через указатель */ |
return 0; |
|
} |
|
Приведем еще пример. Напишем функцию Count(), которая вычисляет количество больших или маленьких латинских букв в строке s. Пусть функции int Big(int c) и int Small(int c) соответственно определяют, является ли символ c большой или маленькой латинской буквой. Если в качестве параметра функции Count() передается адрес функции Big(), то она будет вычислять количество больших латинских букв в строке s. Если же данной функции передать адрес функции Small(), то она подсчитает количество маленьких латинских букв в строке s.
Взаголовке функция Count() имеет такой параметр: int (*flag)(int)
Это значит, что в качестве аргумента функция Count() может получить адрес абсолютно любой функции с тем условием, чтобы она возвращала результат целого типа, а в качестве параметра принимала тип int. Заметим, что вокруг *flag необходимы круглые скобки, чтобы соблюсти соответствующий приоритет операций. Круглые скобки имеют более высокий приоритет, нежели операция звездочка (*). Поэтому если записать
176
int *flag(int)
то это объявление будет означать функцию, которая в качестве параметра получает целое число и возвращает указатель на некоторое целое число.
Функция, которая передается функции Count() в качестве параметра, вызывается в условии if:
if ((*flag)(s[i]))
Разыменовывание функции в данном случае необходимо, чтобы вызвать эту самую функцию.
#include<stdio.h> #define N 200
int Big(int); |
/* прототип функции Big() |
*/ |
int Small(int); |
/* прототип функции Small() |
*/ |
int Count(char *s, int (*)(int)); |
/* прототип функции Count() */ |
int Big(int c)
{
return (c >= 'A' && c <= 'Z');
}
int Small(int c)
{
return (c >= 'a' && c <= 'z');
}
int Count(char *s, int (*flag)(int))
{
int i, n; i = n = 0;
while (s[i] != '\0')
{
if ((*flag)(s[i])) n++;
i++;
}
177
return n;
}
int main( )
{
char s[N]; fgets(s, N, stdin);
printf("%d\n", Count(s, Big)); printf("%d\n", Count(s, Small)); return 0;
}
Приведем также пример использования массива указателей на функции. Определим три функции: Sum, Subtraction, Multiplication,
каждая из которых имеет два целочисленных параметра и возвращает некоторое целое число – результат бинарной операции с данными числами (сумма, разность, произведение). Адреса данных функций будут храниться в массиве f размером 3, который инициализируется следующим образом:
int (*f[3])(int, int) = {Sum, Subtraction, Multiplication};
После того как пользователь введет два целых числа (a и b) и номер арифметической операции над данными числами (0 – сумма, 1 – разность, 2 – произведение), приведенная ниже программа выведет на экран соответствующий результат (a+b, a-b или a*b).
#include<stdio.h>
int Sum(int, int);
int Subtraction(int, int); int Multiplication(int, int);
int Sum(int a, int b)
{
return a + b;
}
int Subtraction(int a, int b)
178