Операция typedef
Любому типу данных, как стандартному, так и определенному пользователем, можно задать новое имя с помощью операции typedef:
typedef <тип> <новое_имя>;
Введенный таким образом новый тип используется аналогично стандартным типам, например, введя пользовательские типы:
typedef unsigned int UINT;
typedef char M_s[100];
декларации идентификаторов введенных типов имеют вид:
UINT i, j; ® две переменные типа unsigned int
M_s str[10]; ® массив из 10 строк по 100 символов
15.4. Указатели на функции
В языке С идентификатор функции является константным указателем на начало функции в оперативной памяти и не может быть значением переменной. Но имеется возможность декларировать указатели на функции, с которыми можно обращаться как с переменными (например, можно создать массив, элементами которого будут указатели на функции).
Рассмотрим методику работы с указателями на функции.
1. Как и любой объект языка С, указатель на функции необходимо декларировать. Формат объявления указателя на функции следующий:
тип (*переменная-указатель)(список параметров);
т.е. декларируется указатель, который можно устанавливать на функции, возвращающие результат указанного типа и которые имеют указанный список параметров. Наличие первых круглых скобок обязательно, так как без них – это декларация функции, которая возвращает указатель на результат своей работы указанного типа и имеет указанный список параметров.
Например, объявление вида: float (* p_f)(char, float);говорит о том, что декларируется указатель p_f, который можно устанавливать на функции, возвращающие вещественный результат и имеющие два параметра: первый – символьного типа, а второй – вещественного типа.
2. Идентификатор функции является константным указателем, поэтому для того, чтобы установить переменную-указатель на конкретную функцию, достаточно ей присвоить ее идентификатор:
переменная-указатель = ID_функции;
Например, имеется функция с прототипом: float f1(char, float);тогда операция p_f = f1; установит указатель p_1 на данную функцию.
3. Вызов функции после установки на нее указателя выглядит так:
(*переменная-указатель)(список аргументов);
или
переменная-указатель (список аргументов);
После таких действий кроме стандартного обращения к функции:
ID_функции(список аргументов);
появляется еще два способа вызова функции:
(*переменная-указатель)(список аргументов);
или
переменная-указатель (список аргументов);
Последнее справедливо, так как p_f также является адресом начала функции в оперативной памяти.
Для нашего примера к функции f1 можно обратиться следующими способами:
f1(‘z’, 1.5); // Обращение к функции по ID
(* p_f)(‘z’, 1.5); // Обращение к функции по указателю
p_f(‘z’, 1.5); // Обращение к функции по ID указателя
4. Пусть имеется вторая функция с прототипом: float f2(char, float);
тогда переустановив указатель p_f на эту функцию: p_f = f2; имеем опять три способа ее вызова:
f2(‘z’, 1.5); // по ID функции
(* p_f)(‘z’, 1.5); // по указателю на функцию
p_f(‘z’, 1.5); // по ID указателя на функцию
Основное назначение указателей на функции – это обеспечение возможности передачи идентификаторов функций в качестве параметров в функцию, которая реализует некоторый вычислительный процесс, используя формальное имя вызываемой функции.
Пример: написать функцию вычисления суммы sum,обозначив слагаемое формальной функцией fun(x), а при вызове функции суммирования передавать через параметр реальное имя функции, в которой запрограммирован явный вид этого слагаемого. Например, пусть требуется вычислить две суммы:
и .
Поместим слагаемые этих сумм в пользовательские функции f1 и f2, соответственно.
При этом для более удобной работы воспользуемся операцией typedef,введем пользовательский тип данных: указатель на функции, который можно устанавливать на функции, возвращающие результат, указанного типа, и имеющие указанный список параметров:
typedef тип_результата (* переменная-указатель)(параметры);
Тогда в списке параметров функции суммирования достаточно указывать фактические ID функций данного типа.
Текст программы для решения данной задачи может быть следующим:
. . .
// Декларация пользовательского типа: указатель на функции,
// возвращающие float результат и имеющие один float параметр
typedef float (*p_f)(float);
float sum(p_f fun, int, float); // Декларации прототипов функций
float f1(float);
float f2(float);
void main(void) {
float x, s1, s2;
int n;
puts(" Введите кол-во слагаемых n и значение x: ");
scanf(“%d%f”, &n, %x);
s1=sum(f1, 2*n, x);
s2=sum(f2, n, x);
printf("\n\t N = %d , X = %f", n, x);
printf(“\n\t Сумма 1 = %f\n\t Сумма 2 = %f", s1, s2);
}
// Функция вычисления суммы, первый параметр которой – формальное имя
// функции, введенного с помощью typedef типа
float sum(p_f fun, int n, float x) {
float s=0;
for(int i=1; i<=n; i++) s+=fun(x);
return s;
}
// Первое слагаемое
float f1(float r) {
return (r/5.);
}
// Второе слагаемое
float f2(float r) {
return (r/2.);
}
В заключение рассмотрим оптимальную передачу в функции одномерных и двухмерных массивов.
Передача в функцию одномерного массива:
void main (void)
{
int vect [20];
…
fun(vect);
…
}
void fun( int v [ ])
{ … }
Передача в функцию двухмерного массива:
void main(void)
{
int mass [ 2 ][ 3 ]={{1,2,3}, {4,5,6}};
…
fun (mas);
…
}
void fun( int m [ ][3])
{ … }
Дата добавления: 2015-09-11; просмотров: 1228;