Tip max(Tip x, Tip y )

{if(x>y)return x;

return y;}

int main()

{int a1=-7,a2=40,res1; float f1=-3.6,f2=-10.5,res2;

res1=max(a1,a2);

res2=max(f1,f2);

printf("res1=%d res2=%.1f\n",res1,res2);

//res1=40 res2=-3.6

return 0;}

При компиляции программы с шаблоном функции, компилятор при вызове функции доопределяет ее. В данном примере при вызове res1=max(a1,a2); сформируется определение int max(int x,int y) и вызовется именно эта функция. При вызове функции res2=max(f1,f2); сформируется определение float max(float x,float y) и теперь вызываться будет данная функция.

Свойства параметров шаблонов:

1. Имена параметров шаблона должны быть уникальными во всем определении шаблона;

2. Список параметров шаблона не может быть пустым;

3. Объявление каждого параметра должно начинаться со слова class;

4. Имя параметра шаблона имеет свойства и права типа, т.е. его именем можно называть типы формальных параметров, тип функции, типы локальных переменные функции. Область видимости параметра шаблона во всем определении функции;

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

template <class A,class B>

B func(A x){…} // Ошибка, параметр В не использован

// в спецификации формальных параметров

Шаблон функции может быть использован и при описании прототипа функции.

template <class A>

void func(A);

Пример. Функция меняет местами значение объектов

#include <stdio.h>

template <class A>

void swp(A *x,A *y) // указатель типа А

{A z=*x; // Шаблон использован в теле функции

*x=*y; *y=z;}

int main()

{double d1=3.3, d2=2.2; swp(&d1,&d2);

printf("d1_n=%. 2lf d2_n=%. 2lf\n",d1,d2);

//d1_n=2.20 d1_n=3.30

char c1='M', c2='Z'; swp(&c1,&c2);

printf("c1_n=%c c2_n=%c\n",c1,c2);//c1_n=Z c2_n=M

char *st1="Москва", *st2="Киев"; swp(&st1,&st2);

printf("st1_n=%s st2_n=%s\n",st1,st2);

//st1_n=Киев st1_n=Москва

swp(st1,st2);

printf("st1_nn=%s st2_nn=%s\n",st1,st2);

// st1_nn=Миев st1_nn=Косква

return 0;}

В этом примере компилятор сформирует следующие определения к функциям:

void swp(double *x,double*y), void swp(char*x,char*y),

void swp(char**x,char**y), void swp(char*x,char*y).

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

Пример. Функция определяет индекс максимального элемента

template <class T>

int func_max(T x[], int n)

{T m=x[0]; int k=0;

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

if(x[i]>max){max=x[i]; k=i;}

return k;}

void main()

{int arr[5]={5,12,40,23,4}, in1, in2;

in1=func_max(arr,5); //in1=2

printf("max1=arr[%d]=%d\n",in1,arr[in1]);

float mas[4]={5.3,1.2,4.0,20.3};

in2=func_max(mas,4); //in2=3

printf("max2=mas[%d]=%.2f\n",in2,arr[in2]);}

В результате работы программы на экран выведется:

max1=arr[2]=40 max2=mas[3]=20.30

2.12 Указатели на функции

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

Указатель на функцию определяется специальным образом:

тип_функции (*имя_указателя)(спецификация_параметров);

где тип_функции – тип возвращаемого функцией значения;

спецификация_параметров – типыформальных параметров;

*имя_указателя – имя указателя на функцию в круглых скобках.

Примеры определения указателей на функцию:

int (*ptrfunc)(int); // указатель на функцию

char (*pf1)(char*);

char* (*pf3)(char*);

Если этот пример записать без скобок, то он будет воспринят компилятором как прототип функции, возвращающей указатель.

int *ptrfunc(int); // прототип

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

Пример.

float mul(float x,float y) {return x*y;}

float sum(float x,float y) {return x+y;}

void main()

{float (*ptrmul)(float,float); // указатель на функцию

float (*ptrsum}(float,float); // указатель на функцию

ptrmul=mul; // указателю присвоили имя функции mul

ptrsum=sum; // указателю присвоили имя функции sum

float f1=3.0,f2=9.0,f3;

f3=(*ptrmul)(f1,f2)+(*ptrsum)(f1,f2);

printf("f3=%.1f", f3); // f3=39.0

float(*ptr)(float,float); // указатель на функцию

ptr=mul; // указателю присвоили имя функции mul

printf("mul=%.1f", (*ptr)(f1,f2)); // mul=27.0

ptr=sum; // указателю присвоили имя функции sum

printf("sum=%.1f",(*ptr)(f1,f2)); }// sum=12.0

Указатели на функцию могут образовывать массив, при этом тип и сигнатура во всех функциях должны быть одинаковые. Синтаксис: тип_функции(*имя_указателя[n])(спецификация_параметров);

где n – размер массива указателей на функции (константное выражение).

Пример. Определен массив из трех указателей на функции, имеющие тип int с параметром типа int

#include <stdio.h>

int f1(int x){return x+x;};

int f2(int x){return x*x;};

int f3(int x){return ~x;}; // побитовое отрицание

void main()

{int(*p[3])(int);

p[0]=f1; p[1]=f2; p[2]=f3;

int a=(*p[0])(5);

int b=(*p[1])(a);

int c=(*p[2])(a);

printf("a=%d b=%d c=%u\n",a,b,c);

}

В результате работы программы на экран выведется:

a=10 b=100 c=65525

Для задания типа указателя на функцию можно применять спецификатор typedef. Синтаксис такой:

// Определение тип указателя на функцию

typedef тип _ функции(*имя _ типа_указателя)(спец_ия _ парам);

// Определение указателя на функцию, массива указателей на функцию

имя_типа_указателя имя_указателя_ф-ции; имя_типа_указателя имя_указателя_ф-ции[n];

Например:

typedef int (*ABC)(int); // тип указателя на функцию ABC

АВС р1; // указатель на функцию р1 типа ABC

АВС р2[4];// массивиз 4 указателей на функцию р2 типа ABC

Пример. Определили массив указателей на функции

#include <stdio.h>

void typ0(){printf("Привет,");}

void typ1(){printf(" дорогой");}

void typ2(){printf(" друг!");}

typedef void (*TYPE)();

TYPE mpf[]={typ0,typ1,typ2};

//void (*mpf[])()={typ0,typ1,typ2};

void main()

{for(int i=0;i<3;i++) mpf[i]();}

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

Пример. Функция суммирует сначала квадраты элементов массива, а затем ‑ их кубы.

#include <stdio.h>

float sqr(float e){return e*e;} // Определение функции

float cube(float y){return y*y*y;} // Определение функции

typedef float(*PF)(float);

float summa(PF f,float a[],int N)// Определение функции

{float sum=0.0;

for(int i=0;i<N;i++) sum+=(*f)(a[i]);

return sum;}

void main()

{float res1, res2, A[3]={1.5,2.0,3.0};

res1=summa(sqr,A,3); //2.25+4+9=15.25

res2=summa(cube,A,3); //3.375+8+27=15.25

printf("res1=%.3f res2=%.3f\n",res1,res2);}

На экран выведется: res1=15.250 res2=38.375

3 Работа с файлами

Функции для работы с файлами в BC31 построены на функциях MS-DOS. Их разделяют на: потоковые и префиксные. Главное их отличие в том, что потоковые функции выполняют дополнительную буферизацию информации. Это приводит к двойной буферизации информации: на уровне MS-DOS и на уровне библиотечной функции (создается регулируемый буфер). Префиксные функции (функции нижнего уровня) не создают дополнительный буфер, они более эффективны при переносе блоков данных по 512 байт.

Мы будем рассматривать потоковые функции работы с файлами. Для пользователей поток – это последовательность байтов, которые передаются в файл или из файла, либо на физическое устройство или с физического устройства (например, монитор, клавиатура). Поток позволяет обрабатывать данные различных размеров и форматов.

Ввод-вывод потока позволяет выполнять следующие действия: 1) открывать и закрывать потоки; 2) создавать и удалять временные потоки; 3) считывать и записывать символы, строки, форматирован­ные и неформатированные данные; 4) анализировать ошибки ввода-вывода потока и условия конца потока (конца файла) и т.д.

3.1 Потоковый ввод-вывод

С началом выполнения программы автоматически открываются стандартные потоки: стандартный ввод (с клавиатуры), стандартный вывод (на экран монитора), стандартный вывод сообщений об ошиб­ках (на экран монитора). Для функций библиотеки <stdio.h> эти потоки stdin, stdout, stderr, которые работают с консолью (клавиатурой и монитором).

Все другие потоки для обмена данными между файлами нужно открыть явно. Причем, эти потоки могут открываться в одном направлении (только для чтения или записи) и в двух направлениях (для чтения и записи). Когда поток открывается, он связывается со структурой определенного типа, содержащей всю информацию для работы с потоком. Тип этой структуры с именем FILE определен в файле <stdio.h> и имеет следующий вид:


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: