- •Конспект лекций Часть 2 Оглавление
- •Часть 2 1
- •8. Указатели
- •8.1. Указатели Понятие указателя
- •Работа с указателями
- •Арифметика указателей
- •Ошибки при работе с указателями
- •If (p1) // Если указатель не равен 0, то все в порядке
- •8.2. Указатели и массивы
- •9. Функции и структура программы
- •9.1. Создание и использование функций Процедурный подход к разработке программ
- •Int Максимум_строки (int Строка)
- •Int Минимум_строки (int Строка)
- •Int Максимум_столбца (int Столбец)
- •Int Минимум_столбца (int Столбец)
- •Определение функций в программе
- •Завершение работы функции (инструкция return)
- •If ( Ошибка )
- •Список параметров функций
- •Обращение к функциям в программе
- •Передача данных по значению
- •Передача данных с помощью указателей
- •Передача данных по ссылке
- •Перегружаемые функции
- •Параметры по умолчанию
- •Функции с переменным числом параметров
- •Рекурсивное использование функций
- •Передача функций в качестве параметров
- •Встраиваемые функции (inline - функции)
- •Прототипы функций
- •9.2. Структура программы. Глобальные и локальные данные (области видимости и время жизни) Структура программы
- •Глобальные и локальные данные
- •Классы памяти
- •Многофайловые проекты
- •10. Структуры, объединения, перечисления
- •10.1. Структуры Определение структур
- •Доступ к полям структур
- •Указатели на структуры
- •Структурные параметры функций
- •Битовые поля структур
- •10.2. Объединения Обычные объединения
- •Анонимные объединения
- •10.3. Перечисления
- •Void WriteColor (тип_Спектр c )
- •11. Организация работы с файлами
- •11.1. Потоки для работы с файлами Общие сведения
- •Пример работы с файлом
- •11.2. Работа с файлами Создание потока, открытие и закрытие файла
- •Запись и чтение данных в текстовых файлах
- •Запись и чтение данных в двоичном режиме
- •If ( !File ) // Проверили удалось ли открыть файл
- •Как обнаружить конец файла?
- •Прямой доступ при работе с файлами
- •If ( !File ) // Проверили удалось ли открыть файл
- •Статус потоков ввода-вывода
- •Некоторые другие функции управления потоками ввода-вывода
- •Примеры по работе с файлами
- •12. Работа с динамической памятью Распределение памяти при работе программы
- •Динамическое выделение и освобождение памяти в стиле c
- •Возможные ошибки при работе с динамической памятью
- •Динамические массивы
- •Одномерные однонаправленные списки
- •Одномерные двунаправленные списки
- •Многомерные списки
Передача функций в качестве параметров
Имя любой функции представляет собой указатель, то есть адрес памяти, определяющий, где находится функция, и задающий точку входа в функцию. Убедиться в этом можно выведя на экран значение имени функции. Например:
cout << C << endl; // C – это функция из предыдущего параграфа
На экране будет отображен в шестнадцатеричном формате адрес точки входа в функцию С, вычисляющую значения биномиальных коэффициентов.
Используя тот факт, что имена функций являются обычными указателями, можно передавать функции в качестве аргументов других функций.
Для этого необходимо специальным образом определить тип данных указателя на функции, передаваемые в качестве аргумента, который должен соответствовать характеристикам передаваемых функций (тип возвращаемого значения, параметры функции).
Рассмотрим пример:
double add (double a, double b)
{
return a + b;
}
double mul (double a, double b)
{
return a * b;
}
typedef double (*f_Ptr) (double, double);
double oper (f_Ptr F, double a, double b)
{
return F (a, b);
}
int main ()
{
cout << oper (add, 20, 30) << endl; // 50
cout << oper (mul, 20, 30) << endl; // 600
return 0;
}
В этом примере имеется некоторая универсальная функция oper, возвращающая значение вычисленной функцииF для заданных значенийa и b. ФункцияF, которую необходимо вычислить, передается в функциюoperчерез параметр этой функции. Ключевые элементы этого алгоритма выделены красным цветом. Для того, чтобы параметрFфункцииoperмог принимать в качестве аргумента другую функцию, с помощью инструкцииtypedefопределяется тип данных указателяf_Ptrна функции, возвращающие значение типаdoubleи имеющие два параметра типаdouble.
Этот пример носит исключительно демонстрационный характер. На самом деле, с помощью этого приема удается реализовывать сложные универсальные алгоритмы обработки различных данных.
Несколько слов об инструкции typedef. С помощью этой инструкции можно определять новые типы данных и переопределять уже имеющиеся. Например: довольно утомительно в тексте программы многократно использовать название типа данныхunsigned short. С помощью инструкцииtypedefможно упростить набор текста программы:
typedef unsigned short UnS;
Теперь везде в тексте программы можно использовать вместо типа unsigned short тип UnS.
Однако увлекаться такими “переименованиями” не следует. В основном инструкция typedef используется при определении новых пользовательских типов данных.
Встраиваемые функции (inline - функции)
Использование функций снижает быстродействие программы. Это объясняется затратами времени на вызов функций (работа со стеком программы). Если в программе используется большое количество вызовов небольших функций, то быстродействие программы может существенно снизиться.
Избежать этого можно применением, так называемых, встраиваемых функций. Встраиваемые функции задаются ключевым словом inline:
inline int ReadInt(char *S) // Ввод целых значений по текстовому запросу S
{
cout << S;
int I;
cin >> I;
return I;
}
{
……..
int Vozrast = ReadInt (“Сколько Вам лет? ”);
……..
int Kurs = ReadInt (“На каком курсе Вы учитесь? ”);
……..
int Ocenka = ReadInt (“Сколько у Вас отличных оценок в последней сессии? ”);
……..
}
При определении inline-функции компилятор заменяет (если это можно сделать) каждый вызов этой функции кодом функции. При такой подстановке код программы увеличивается, но зато экономится время на вызов встроенной функции.
Обычно в качестве встраиваемых функций используются часто вызываемые небольшие по объему функции. Использование больших по объему часто вызываемых встраиваемых функций может привести к чрезмерному росту объема программы, что тоже может быть нежелательно. Поэтому к использованию встраиваемых функций надо подходить разумно.
Некоторые компиляторы накладывают определенные ограничения на содержание встраиваемых функций. К таким ограничениям обычно относятся использование внутри встраиваемых функций:
рекурсии;
циклов, переключателей, инструкций goto;
статических (static) переменных.
В любом случае компилятор сам определит можно ли встраивать функцию или необходимо вызвать ее обычным способом.