
- •«Национальный исследовательский томский политехнический университет»
- •Матричные исчисления с использованием функций пользователя
- •Понятие функции
- •Определение пользовательской функции
- •Передача значений в функцию: фактические и формальные параметры
- •Локальные и глобальные переменные
- •Возврат результатов из функции
- •Return выражение;
- •Адреса и указатели
- •Размещение функций в файле
- •Массивы как параметры функций
- •Двумерный массив как параметр функции
- •Краткие выводы
-
Размещение функций в файле
1) Каждая функция описывается ОТДЕЛЬНО, это отдельные "строительные блоки".
2) В принципе все функции равноправны, т.е. из любой функции можно вызвать любую другую. Но не надо забывать при этом, что функция main() выполняется первой, и обращаться к ней из других функций без веских причин, мягко говоря, не стоит.
3) Описана функция (её тип и типы аргументов) должна быть ДО первого (от начала файла) её использования. Здесь возможно 2 варианта:
a) привести описание самой функции ДО той функции, из которой она вызывается;
b) привести описание прототипа функции ДО вызова – в этом случае описание прототипа может быть как локальным (внутри вызывающей функции), так и глобальным (например, до описаний всех функций). Под прототипом здесь понимается описание типа функции, её имени и типов всех аргументов (имена аргументов можно не приводить). В этом случае сама функция может быть описана в любом месте файла. Не забудьте, что описание прототипа функции – это отдельный оператор, который должен заканчиваться соответствующим символом.
Оба варианта эквивалентны.
Пример:
Вариант а) |
Вариант b) |
int pr (float a, int* b) { ... тело функции }
void main () {... a = pr (bb, &cc); } |
int pr (float, int*);
void main () {... a = pr (bb, &cc); }
int pr (float a, int* b) { ... тело функции } |
-
Массивы как параметры функций
Имя массива является указателем на первый элемент массива. Поэтому при вызове функций readvektor()и poiskmax() в примере п.9.2.1 в качестве первого фактического параметра передавалось на самом деле не имя соответствующего массива, а адрес его первого элемента. Соответственно в описании этих функций использовалась форма тип имя_массива[], что является эквивалентом оператору тип *имя. То есть можно было, например, заголовок функции написать следующим образом:
double poiskmax(double *y, int m);
а не
double poiskmax(double y[], int m);
просто во втором случае описание y[] лишний раз напоминает о том, что указатель y ссылается на массив. Все остальные действия с массивом y в функции poiskmax() остаются без изменений, в частности, обращение к элементам массива.
Более того, вообще говоря, вместо y[i] можно использовать *(y+i).
-
Двумерный массив как параметр функции
Двумерный массив можно рассматривать как массив указателей или указатель на указатель.
Одним из способов избежать неприятностей при необходимости работать с двумерными массивами как аргументами функции является явное указание, как минимум, величины второй размерности (т.е. количества столбцов) при объявлении функции:
void vv (int x[][10], int m, int n)
{...
.. x[i][j] .. // использование элемента массива x[i][j]
}
void main ()
{
int a[20][10], k1=5, k2=4;
vv (a, k1, k2);
...
}
Таким образом, компилятор "поймет", что массив следует разбить на строки по 10 столбцов. Или, если описать формальный параметр как одномерный массив, необходимо будет обращаться к каждому элементу массива, предварительно вычисляя его адрес, например:
void vv (int x[], int m, int n)
{...
.. x[i*n+j] .. // использование элемента массива x[i][j]
...
}
...
vv (a[0], k, 10); /* обращение к функции; здесь в качестве первого аргумента передаём адрес первой строки; количество столбцов должно соответствовать заданному в описании массива!!! */
...
Это связано с тем, что в памяти массив располагается, занимая последовательные ячейки памяти. Так, массив z[2][3] располагается в памяти следующим образом:
z[0][0] z[0][1] z[0][2] z[1][0] z[1][1] z[1][2]
Тогда z[0]&z[0][0], z[1]&z[1][0] и т.д. Вычислить адрес z[1][0] можно, добавив к адресу самого первого элемента массива количество столбцов из описания: z+3.
Пример:
# include <stdio.h>
int a[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12}; /* массив, описанный глобально, можно инициализировать таким способом */
void vv (int x[], int m, int k, int n)
{int i, j;
for (i=0; i<m; i++)
{
for (j=0; j<k; j++)
printf ("x[%d][%d]=%d ",i,j,x[i*n+j]); //или *(x+i*n+j)
printf("\n");
}
}
void main ()
{
int nn = 2, kk = 3;
vv (a[0], nn, kk, 4);
}