Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

книги / Язык Си

..pdf
Скачиваний:
6
Добавлен:
20.11.2023
Размер:
7.64 Mб
Скачать

Наличие круглых скобок в имени указателя на функцию обя­ зательно.

Перед использованием указателя на функцию необходимо его инициализировать, т.е. присвоить ему адрес первой ячейки облас­ ти памяти, где расположена функция.

2

0.5

Пример. Вычислить интегралы

методом прямоугольников.

#include<stdio.h>

#include<conio.h>

#include<math.h>

//Объявление указателя на функцию double (*f)(double);

//Прототип функции интегрирования //методом прямоугольников

double rectangle(double, double, double =0.01);

//Подынтегральная функция первого интеграла double fl(double x)

{

return x/pow(x*x+l,2);

}

//Подынтегральная функция второго интеграла double f2 (double x)

{

return 4*cos(x)*cos(x);

}

//Главная функция main()

{f=fl; //настраиваем указатель на первую функцию

//вычисляем интеграл и печатаем ответ printf("S_fl=%f\п",rectangle(-1,2) );

f=f2; //настраиваем указатель на вторую функцию

//вычисляем интеграл и печатаем ответ printf("S_f2=%f",rectangle(0,0.5));

getch() ; return 0;

}

// Интегрирование методом прямоугольников double rectangle(double a, double b, double h) {double x=a,s=0;

while(x<=b)

{s+=(f(x)+f(x+h))/2*h;

x+=h;

}

return s;

Обращаем ваше внимание на то, что указатель на функцию в данном примере является глобальным, т.е. виден во всех функци­ ях: main, rectangle и т.д.

Если не использовать указатель на функцию, тогда в функ­ ции, вычисляющей интеграл (rectangle), нужно было бы ввести еще один параметр (например, пит), по которому можно ориенти­ роваться, какой интеграл вычислять.

double rectangle(int num, double a, double b, double h)

{double x=a,s=0;

if(num==l)

while(x<=b)

{s+=(fl(x )+f1 (x+h))/2*h;

x+=h;

}

else if(num==2)

while(x<=b)

{s+=(f2 (x)+f2(x+h))/2*h;

x+=h;

}

return s;

}

Таким образом, гораздо удобнее использовать только одно имя - указатель, который предварительно настроен на нужную функцию.

Также из указателей на функции можно формировать масси­ вы. Объявление массива указателей на функции имеет вид

тип (*имя массива[размер])(список параметров);

Вычислим интегралы из предыдущего примера.

#include<stdio.h>

#include<conio.h>

#includecmath.h>

//глобальный массив указателей на функции double (*f[2])(double);

double rectangle (int,double, double, double=0.01);

double fl(double x) {return x/pow(x*x+l,2) ;

}

double f2(double x) {return 4*cos(x)*cos(x);

}

main()

{//инициализация элементов массива //указателей на функции

f [0]= f 1;

f[l]=f2;

//выполнение интегрирования printf("S_fl=%f\n",rectangle(0,-1,2)); printf("S_f2=% f",rectangle(1,0,0.5));

getch(); return 0;

}

double rectangle(int i, double a, double b, double h)

{double x=a,s=0; while(x<=b)

{s+=(f[i](x)+f[i](x+h))/2*h;

x+=h;

}

return s;

}

В данном случае в функцию rectangle приходится передавать номер элемента /, соответствующего интегрируемой функции, од­ нако все указатели можно задать сразу.

Указатель на функцию можно использовать в качестве пара­ метра другой функции.

#include<stdio.h>

#include<conio.h>

#include<math.h>

double rectangle(double (*f)(double), double,double,double=0.01);

double fl(double x) {return x/pow(x*x+l,2);

}

double f2(double x) {return 4*cos(x)*cos(x);

}

main() {printf("S_fl=%f\n",rectangle(fl,-1,2) ); printf("S_f2=%f", rectangle(f2,0,0.5));

getch(); return 0;

}

double rectangle(double (*f) (double x) , double a,double b,double h)

{double x=a,s=0; while(x<=b) {s+=(f(x)+f(x+h))/2*h;

x+=h;

}

return s;

}

Теперь при вызове функции, выполняющей интегрирование, можно указывать и имя подынтегральной функции rectcmgle(f[, -1,2). Очевидно, что из всех рассмотренных вариантов данный способ наиболее удобен.

10.5. Ссылки

Ссылки - это специальный тип указателя, введенный в Си++, который позволяет работать с указателем как с переменной. Объ­ явление ссылки делается с помощью операции ссылки, обозначае­ мой амперсандом (&) - тем же символом, который используется для адресации. Если в программе имеется указатель на перемен­ ную какого-то типа:

float *р;

то можно создать ссылку на переменную оператором:

float &а=*р;

Ссылка является альтернативным именем переменной. При объявлении она должна быть инициализирована, и затем ее значе­ ние уже нельзя изменить.

Пример. Операции с ссылкой.

#include<stdio.h>

#include<conio.h> main()

{int a=6,b=2;

int &c = а; //с является ссылкой на a

c=b; //приводит к a=b c+=2; //приводит к a+=2

printf(Mc=%d\n",с); //напечатает c=4 printf("a=%d\n",a); //напечатает a=4 getch();

return 0;

}

В данном примере с всегда ссылается на а и все действия вы­ полненные над с отражаются на а.

10.6. Динамическое выделение памяти

Динамическое распределение памяти широко используется для экономии вычислительных ресурсов. Например, если неизвест­ но, сколько в массиве будет элементов, то можно заранее с запасом отвести место под множество элементов:

float а [100000];

Однако этот вариант плохой, поскольку связан с излишшщи затратами памяти. Например, при выполнении программы мо^кег понадобиться всего два элемента. Таким образом, в подобных ситуа­ циях лучше использовать динамическое распределение пам ^и, создавая массив нужного размера по ходу выполнения программы,

Работа с динамической памятью осуществляется посредством библиотечных функций malloc, calloc, realloc, free и операторов new, delete.

10.6.1. Библиотечные функции распределения памяти

Для использования библиотечных функций распределения динамической памяти нужно подключить файл stdlib.h (табл. 10.1).

Т а б л и ц а 10.1

Функции распределения динамической памяти

Функция

Прототип функции, назначение

n/n

 

void *mal!oc(unsigned s) выделяет динамическую

 

 

1

malloc

память длиной в s байт. При неудачной попытке

 

 

выделения памяти возвращает NULL

 

 

void ^calloc(unsigned n, unsigned m) выделяет дина­

 

calloc

мическую память под п элементов по т байт каж­

2

дый, при этом автоматически заполняет нулями

 

 

выделенную память. При неудачной попытке выде­

 

 

ления памяти возвращает NULL

 

 

void *realloc(void *р, unsigned т) сжимает или уве­

 

 

личивает размер динамической памяти, ранее вы­

3

realloc

деленной под переменную р, до т байт. Если ука­

 

 

затель */? равен NULL, т.е. память ранее не выделя­

 

 

лась, то функция выполняется как malloc

4

free

void free(void *р) освобождает память, ранее вы­

деленную под переменнуюр

 

 

Пример. Выделение памяти под одномерный массив.

#in c lu d e < std io . h >

#in c lu d e < c o n io . h>

#in c lu d e < s t d lib . h >

main ()

{in t i , k , n ;

in t *a;

p u t s ("Введите число элементов"); s c a n t ("%d", &n);

к = s i z e o f ( i n t ) ;

//операция

s iz e o f определяет,

 

//ско лько

байтов

выделяется

 

/ /под указанный

тип данных

//вы деление динамической памяти

 

//п о д одномерный массив

 

 

а = ( i n t * ) m a l l o c (п*к);

 

 

//в в о д элементов

массива с клавиатуры

f o r ( i= 0 ;i< n ;i+ + )

 

 

 

{printf("a[%d]=",i) ; scanf("%d",&a[i]);

//печать массива

for(i=0;i<n;i++)

printf("%3d",a[i]);

/ / освобождение динамической памяти

free(a);

getch();

return 0;

}

Пример. Печать неинициализированного динамического мас­

сива.

#include<stdio.h>

#include<conio.h>

#include<stdlib.h>

main()

{int i,k,n,*a;

puts("Введите число элементов"); scanf("%d",&n);

к = sizeof(int);

a = (int*)malloc(n*k) ; for(i=0;i<n;i++) printf ("%3d",a[i] ); printf("\nn); free(a);

puts("Введите число элементов"); scanf("%d",&n);

a = (int*)calloc(n,k); for(i=0;i<n;i++) printf("%3d",a[i]); free(a);

getch(); return 0;

}

Результат на экране:

Введите число элементов

3

-842150451-842150451-842150451

Введите число элементов

3

0 0 0

Первый раз мы выделили память под массив с помощью функции malloc, а затем распечатали массив. Поскольку мы не за­ полнили массив значениями, там оказались большие случайные числа (в вашем случае они могут быть другими). Второй раз мы выделили память под массив с помощью функции calloc, и она сама заполнила массив нулями.

Пример. Изменение размера массива по ходу выполнения про­ граммы.

#include<stdio.h>

#include<conio.h>

#include<stdlib.h>

main ()

{int i,k,n,*a;

//выделяем память под массив puts("Введите число элементов"); scant("%d",&n);

к = sizeof(int);

a = (int*)calloc(n,k);

//вводим элементы массива с клавиатуры for(i=0;i<n;i++)

{printf("a[%d]=",i);

scanf("%d",&a[i]);

}

//печатаем массив for(i=0;i<n;i++) printf("%3d",a[i]); print f (1\n" );

//задаем новый размер массива puts("Введите число элементов "); scanf("%d",&n);

а = (int*)realloc(a,n);

//заполняем массив значениями for(i=0;i<n;i++) {printf("a[%d]="/i);

scanf("%d" , &a[i]);

}

//печатаем массив for(i=0;i<n;i++) printf("%3d",a[i] );

free(а); //освобождаем память

getch() ; return 0;

}