Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
часть 2.doc
Скачиваний:
0
Добавлен:
01.07.2025
Размер:
367.1 Кб
Скачать

Функции. Использование массивов и функций в качестве формальных параметров

Цель работы

Изучение способов обработки массивов данных с помощью функций.

Методические указания

Передача массивов в качестве аргументов функции. Простой способ передачи массива функции демонстрирует пример 1:

#include <stdio.h>

void print(int* massiv); /*Прототип функции*/ void main(void) { intv[3],i; for (i=0; i<3; i++) v[i]=i;

print(v); /*Вызов функции и передача адреса массива*/ for (i-0; i<3; i++) printf("%d ",v[i]);

} void print(int mass[3]

{ int i; for(i=0;i<3;i++) printf("%d ",mass[i]++); printf("\n");}

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

012 123.

До вызова функции print массив v содержал элементы 0, 1 и 2. Функция print приняла адрес массива v и присвоила этот адрес переменной mass. После вы­вода каждое значение массива mass (первая строка результатов) увеличивает­ся на единицу. Поскольку переменная mass указывает на массив v, на единицу увеличились элементы массива v (вторая строка результатов). Другими сло­вами, массивы v и mass расположены в одной и той же области памяти, и из­менение в mass означает изменение в v.

В этом примере размер массива, обрабатываемого функцией print, дол­жен быть известен до компиляции. Такая реализация необязательна: функции print нужен лишь начальный адрес массива, а количество элементов можно передать еще одним аргументом, как в примере 2:

void print(int* massiv, int lines); /*Прототип функции*/

void main(void)

{ int v[3],i; for (i=0; i<3; i++) v[i]=i;

print(v,3); /*Передаем адрес массива и количество элементов*/ for(i=0;i<3;i++)printf(“%d",v[i]);

}

void print(int mass[], int m)

{ int i; for (i = 0); i<m; i++) printf("%d ",mass[i]++); printf("\n");}

Результаты, выводимые на экран, не изменились. Но теперь, при изме­нении в функции main размера массива v нет необходимости что-либо менять в функции print. Как и в примере 1, массивы v и mass расположены в одной и той же области памяти, и изменение в mass означает изменение в v.

Общее в примерах 1 и 2 в том, что массив в функцию можно передать и как ссылку на его первый элемент. Например, в примере 2 массив в функцию print можно передать так:

Пример 3.

void print(int* mass, int m)

{ int i; for (int i = 0; i<m; i++) printf("%d ",mass[i]++); printf("\n");}

В случае многомерных массивов многое совпадает со случаем одномер­ного массива. Рассмотрим примеры с двумерным массивом:

Пример 4.

#include <stdio.h>

void print(int matrix[2][3]); /*Прототип функции*/

void main(void)

{ int v[2][3], i, j; for (i=0; i<2; i++) for (j=0; j<3; j++) v[i][j]=i+j;

print(v); /*Вызов функции и передача адреса массива*/

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

{ for (j=0;j<3;j++) printf("%d",v[i] [j]); printf("\n");}

} void print(int mass[2][3])

{ inti.j;

for (i=0; i<2; i++) { for (J=0;j<3;j++) printf("%d ",mass[i][j]++); printf("\n");}

}

Функция print выводит на экран элементы массива mass (первые две строки) и увеличивает их значения на единицу (последние две строки).

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

Можно поступить так: сообщить компилятору, что первый параметр -массив строк по 3 элемента, а второй - количество строк.

Пример 5.

void print(int matrix[][3], int lines); /*Прототип функции*/

void main(void)

{ int v[2][3], i, j; for (i=0; i<2; i++) for (j=0; j<3; j++) v[i] [j] = i+j;

print(v,2); /*Передаем адрес массива и количество строк*/'

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

{ for (j=0; j<3; j++) printf("%d ",v[i][j]); printf("\n");}

}

void print(int mass[][3], int m) { int i, j;

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

{ for (j=0; j<3; j++) printf("%d ",mass[i]|j]++); printf("\n");}.

Результаты не изменились. Но теперь при изменении количества строк

матрицы v в функции main не нужно изменять что-либо в функции print.

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

Пример 6. void print(int* massiv, int columns);

void main(void) { int v[2][3], i, j; for (i=0; i<2; i++) for (j=0; j<3; j++) v[i][j]=i+j;

for (H); i<2; i++)

print(v[i],3); /"'Передаем адрес строки и количество элементов*/ for (i=0; i<2; i++) { for (j=0; j<3; j++) printf("%d ",v[i][j]); printf("\n");}}

void print(int* mass, int n)

( int i; for (i-0; i<n; i++) printf("%d ",mass[i]++); printf("\n");}

Результаты, выводимые на экран, не изменились. Но теперь, при изме­нении любого размера матрицы v нет необходимости что-либо менять в функ­ции print. При таком подходе мы накладываем ограничения на работу функ­ции print: она работает только с одной строкой матрицы.

В примере 7 функция print свободна от таких ограничений и в качестве аргументов принимает адрес первого элемента, количество строк и столбцов матрицы:

Пример 7. void print(int** matrix, int lines, int columns);

void main(void) { int v[2][3], i, j; for (i=0; i<2; i++) for 0=0; j<3; j++) v[i](j]=i+J;

print ((int**)v, 2, 3);

for (i=0; i<2; i++) { for j=0;j<3;j++) printf(“%d",v[i][j]); printf("\n");}}

void print(int** mass, int m, int n)

{ int *p=(int*)mass, i, j;

for (i=0; i<m; i++) { for (j=0;j<n;j++) printf(“%d", р[i*п+j]++); printf("\n");}}

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

массива. Элементы двумерного массива в памяти расположены построчно и непрерывно. То есть, с точки зрения размещения в памяти, двумерный массив представляет собой одномерный массив. Для доступа kj-му элементу i-ой строки достаточно вычислить относительный адрес i*n+j и отсчитать его от первого элемента p[i*n+j].

Результаты, выводимые на экран, не изменились. Но теперь, при изме­нении любого размера матрицы v нет необходимости что-либо менять в функ­ции print, и эта функция свободна от предыдущих ограничений.

Описание имени типа директивой typedef

Директива typedef позволяет связать любой основной или производный тип с некоторым именем, а затем использовать это имя типа. Такое описание используется для создания более коротких или осмысленных имен типов.

Синтаксис описания имени типа:

typedef <описание_типа> <имя1>[,<имя2>...]:

typedef- это ключевое слово компилятора Си.

<описание_типа> может быть именем известного типа (даже из списка <имя1>[,<имя2>...]) или описанием типа. Например:

typedef int MyInt;

typedef MyInt MyInt;

Имена <имя1>[,<имя2>...] становятся синтаксически эквивалентными ключевому слову и именуют тип, указанный в <описание_типа>. Они не мо­гут совпадать с именами переменных, формальных параметров, перечисли­мых констант и функций. Имена могут быть переопределены внутри блоков программы. Имена для типов указателя, структуры или совмещения могут быть объявлены прежде чем эти типы будут описаны. Имена типов можно использовать в описаниях переменных и функций как ссылки на этот тип.

Примеры:

typedef int WHOLE;

typedef void DRAW(int, int);

typedef WHOLE MASSIV[10];

В первом примере объявляется WHOLE как синоним для int.

Во втором примере представлен тип DRAW для функции не возвра­щающей значения и требующей два аргумента типа int. Это означает, напри­мер, что объявление DRAW box; эквивалентно объявлению void box(int, int);

В третьем примере описывается тип массива десяти целых элементов с именем MASSIV, которое можно использовать так:

MASSIV myList;

Указатели на функцию, их использование в качестве параметра функции

Любую функцию можно вызвать или получить ее адрес. Имя "функции, возвращающей ...", всегда преобразуется в "указатель на функцию, возвраща­ющую ..." (т.е. имя функции это то же, что и указатель на функцию), если только оно не является аргументом вызова другой функции. Можно явно описать «указатель на функцию». Синтаксис описания такой переменной сле­дующий:

<тип> (*<имя>)(<список_формальных_параметров>);

<тип> - это тип возвращаемого значения функций, на которые может затем указывать <имя>.

<список_формальных_параметров> - это список формальных парамет­ров функций, на которые может затем указывать <имя>. Список задается по правилам объявления функций.

Пример.

int myfunc1( double*, int, int); /*Первая функция*/

int myfunc2( double*, int, int); /*Вторая функция*/

int (*myfunc)( double*, int, int); /*Указатель на такие функции*/

void main(void)

{ double x=2.3; int i=4, k=5;

myfunc=&myfunc1; /*Берем адрес вызываемой функции*/ i=(*myfunc)(&x, i, k); /*Вызываем эту функцию (myfunci)*/

}

Для вызова функции по указателю нужно выполнить операцию обратной ссылки *<имя>. Поскольку круглые скобки справа от имени имеют больший приоритет, чем звездочка слева, то операцию обратной ссылки нужно заключить в круглые скобки: (*<имя>). Правила передачи аргументов одинаковы для вызовов по имени функции и по указателю на функцию.

Удобно описать имя типа "указатель на функцию". Например,

typedef int (*MYFUNC)( double*, int, int);

Это имя типа можно использовать различным образом. Например,

MYFUNC myfunc, myfuncs [] =

{ myfunc1, myfunc2, myfunc3, myfunc4 };

означает, что myfunc является указателем на функции, a myfuncs – массивом указателей на функции. Можно описать и инициализировать указатели:

MYFUNC* funcs= myfuncs.

Переменная funcs необязательна для вызова функций. Для вызова можно пользоваться и массивом myfuncs. Но удобно одновременно хранить адрес вызываемой функции и адрес массива указателей на функции.

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

(*funcs[i]) (&x, i, k); /*Вызываем функцию myfunci*/

Указатель на функции может быть формальным параметром функции. Например,

int func( char* с, int sz, MYFUNC f)

{…

if((*f)(&x,i,k)>0)...}

void main(void)

{ …

к=func("Вызов с первой функцией", 15, myfunc1);

}

Массив указателей на функции также может быть формальным пара­метром функции. Например,

typedef MYFUNC funcs[4];

int func( funcs);

функция func принимает массив из 4 указателей на функции и возвращает целое. Функции могут возвращать указатели на функции.

Например,

typedef int F(void);

F*g(...) {/*..*/} или MYFUNC fnctn(...);

Пример выполнения задания

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

#include <stdio.h> /*Прототипы функций*/ double Add(double, double);

double Sub(double, double);

/"'Описание имени типа указателя на функции*/ typedef double (*MYFUNC)(double, double);

/*Прототип функции, которая вызывает указанную параметром функцию*/ void function(char*, double, double, MYFUNC f);

void main(void) { function("Сумма 4 и 2 равна:", 4., 2., Add);

function("Разность 4 и 2 равна:", 4., 2., Sub);

} double Add(double x, double y) { return x+y;}

double Sub(double x, double y) { return x-y;}

void function(char* str, double x, double y, MYFUNC f)

{ printf("%s %lf\n", str, (*f)(x,y));}

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

Сумма 4 и 2 равна: 6 Разность 4 и 2 равна: 2.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]