Описание функции
Форма описания функции:
тип имя (тип1 арг1, тип2 арг2,...) /*заголовок функции*/
{
описание 1; /*описание
... внутренних переменных,
описание M; массивов*/
оператор 1; /*выполняемая
... часть*/
оператор N;
}
Заголовок функции:
тип– это тип возвращаемого функцией значения (результата) через ее имя.
Определенный тип – int, float, char,...
Неопределенный тип – void
Если тип не указан, то по умолчанию – int.
main() à int main()
void main()
имя – это идентификатор для обозначения функции
типI аргI,...– описания аргументов функции
типI – тип аргумента
аргI – имя аргумента
Функция может не иметь аргументов: тип имя ()
Примеры:
float my_func1(int k, float c, float d, char q)
{
...
}
void ftc(float a[], float *ptk, int n, long s)
{
...
}
Нельзя написать:
float my_func1(int k, float c,d, char q)
{...}
Описание функции может быть записано до или после функции main(), но не внутри ее:
1. До main():
При этом ничего дополнительно не надо записывать, т. к. компилятор сначала рассматривает f1(), а потом main().
2. Функция – после main()
void f1(int, float, chat, float *); /*прототип функции*/
void main()
{
/*прототип функции можно поместить здесь*/
...
вызов f1(...);
...
}
/*описание функции f1*/
void f1(int k, float x, char p, float *ptm)
{
...
}
Вызов функции
При вызове функции, ей задаются конкретные аргументы (фактические параметры), для которых функция выполняет вычисления. Есть 2 формы вызова функций:
1. Функция имеет тип void. Вызов является самостоятельным оператором:
имя (факт_арг1,факт_арг2,...);
Фактические аргументы должны соответствовать списку аргументов в описании функции (по количеству, порядку следования, типам).
float z=1.5;
float *pts;
f1(20,z,’A’,&z);
f1(30,*pts,’N’,pts);
2. Функция имеет определенный тип (не void). Вызов не является самостоятельным оператором. Он записывается внутри другого оператора (присваивания, if, printf,...):
|
u=имя(факт_арг1,факт_арг2,...);
if(имя(факт_арг1,факт_арг2,...)...){...}
float fsum(float a, float b)
{
return a+b;
}
/*Вызов float fsum:*/
float x=5.5,y=7.1,z;
z=fsum(x,y); /* z==12.6 */
Варианты описания и использования функций
1. Функция возвращает результат через свое имя.
В описании функции указывается определенный тип возвращаемого функцией значения – int, float, double, char,... Внутри функции должен быть оператор return(a), где а –выражение для вычисления значения результата.
Вызов функции не является самостоятельным оператором. Он записывается внутри другого оператора (присваивания, if, printf,...).
Пример: вычислить функцию z=f(x,a,y)
для исходных данных: 1) x=5.3; a=-2.1
2) x=3.7; a=7.6
#include<stdio.h>
#include<math.h>
#include<conio.h>
/*Описание функции fz */
float fz(float x, float a)
{
float y;
y=1.7*log(fabs(x+2.5*a));
if(a>=x) return(a*exp(y*sin(2*x)));
else return(2.9*cos(a*y));
}
/*Главная функция main() */
void main()
{
float d,h;
d=fz(5.3,-2.1);
h=fz(3.7, 7.6);
printf(“x=5.3, a=-2.1, d=%3f\n”,d);
printf(“x=3.7, a= 7.6, h=%3f\n”,h);
printf(“fz(6.5,-3.4)=%3f\n”,fz(6.5,-3.4));
}
2. Функция возвращает одно или несколько значений через аргументы
В описании функции указывается тип – void (неопределенный). Аргументы, через которые будут возвращаться результаты, описываются как указатели:
void fun(int k, float *ptf, float z)
Вызов функции будет самостоятельным оператором. В качестве фактических аргументов для возвращаемых значений записываются адреса переменных:
float f;
fun(20, &f, 5.25);
Необходимость использования указателей, а не имен переменных связана с тем, что при вызове функции в стеке (специальная динамическая область памяти для временного хранения промежуточных данных) создаются копии аргументов функции. Все операции функция выполняет с этими копиями. При выходе из функции стек очищается, копии стираются.
|
Если мы напишем:
void f(int arg1, int arg2, int arg3)
{
arg3=arg1+arg2;
}
то фактически выполнится следующая операция:
Копия_arg3 = Копия_arg1+Копия_arg2;
и значение arg3 не изменится.
Если же запишем следующим образом:
void f(int arg1, int arg2, int *arg3)
{
*arg3=arg1+arg2;
}
То будет выполнена запись суммы по адресу, заданному в копии параметра *arg3:
В результате значение суммы аргументов arg1 и arg2 будет вычислено правильно и сохранено в памяти.
Пример:
Составить функцию, которая меняет местами значения аргументов X и Y
#include<stdio.h>
#include<conio.h>
/* Функция без указателей */
void obmen1(float x, float y)
{
float tmp;
tmp=x;
x=y;
y=tmp;
}
/* Функция с указателями */
void obmen2(float *ptx, float *pty)
{
float tmp;
tmp=*ptx;
*ptx =* pty;
*pty=tmp;
}
/* Главная функция */
void main()
{
float g=7.5, h=11.2;
obmen1(g, h);
printf(“Ф-ция без указателей\ng=%.1f h=%.1f\n”,g, h);
obmen2(&g,&h);
printf(“Ф-ция c указателями\n g=%.1f h=%.1f\n”,g, h);
}
На экране получим:
Ф-ция без указателей
g=7.5 h=11.2 Результат неправильный
Ф-ция с указателями
g=11.2 h=7.5 Результат правильный
3. Аргументом функции является одномерный массив.
Описание функции. Такой аргумент можно описать двумя способами:
1) как массив с неопределенным количеством элементов:
void fun(float a[], int n,…);
где n – количество элементов массива a.
Внутри функции элементы массива записываются как обычно: a[i]
2) как указатель на начало массива:
void fun(float *pta, int n,…);
где n – количество элементов массива.
Внутри функции элементы массива записываются через указатель: *(pta+i) à a[i]
|
Примечание: Возможен 3-й способ описания массива с указанием фиксированного количества элементов:
void fun(float a[10],…);
Этот способ имеет недостаток. Функция может работать только с массивами длиной 10 элементов.
Вызов функции. При вызове функции для 1 и 2 способов на первом месте указывается имя массива, на втором – количество элементов:
float z[40], t[200];
fun(z, 40,…);
fun(t, 200,…);
Пример. Заданы три массива f[8], g[12], h[15], элементы которых вычисляются по формулам:
f[i]=1.7*i*cos(2.5*i)
g[j]=2.9*j*cos(3.1*j)
h[l]=3.5*l*cos(5.4*l)
Для каждого массива вычислить: среднее значение его элементов (sf, sg, sh) и количество элементов, больших среднего значения (kf, kg, kh). В данной задаче целесообразно использовать функцию, которая будет выполнять следующие действия:
а) формирование массива Х из n элементов по формуле:
x[i]=a*i*cos(b*i), i=1..n
б) вычисление среднего значения sx:
в) подсчет Кх – количества xi>sx
Текст программы для 3-го варианта:
#include<stdio.h>
#include<math.h>
#include<conio.h>
/* Описание функции */
void fmas(float x[], int n, float a, float b, float *pt_sx, int *pt_kx)
{
int i, kx=0;
float sx=0;
for(i=0;i<n;i++)
{
x[i]=a*i*cos(b*i);
printf(%.3f “,x[i]);
sx+=x[i];
}
printf(“\n”);
sx/=n;
for(i=0;i<n;i++)
if(x[i]>sx) kx++;
*pt_sx=sx;
*pt_kx=kx;
}
/* Главная функция */
void main()
{
float f[8], g[12], h[15], sf, sg, sh;
int kf, kg, kh;
clrscr();
printf(“Массив F из 8 элементов\n”);
fmas(f, 8, 1.7, 2.5, &sf, &kf);
printf(“sf=%.3f kf=%d\n”,sf,kf);
printf(“Массив G из 12 элементов\n”);
fmas(g, 12, 2.9, 3.1, &sg, &kg);
printf(“sg=%.3f kg=%d\n”,sg,kg);
printf(“Массив H из 15 элементов\n”);
fmas(h, 15, 3.5, 5.4, &sh, &kh);
printf(“sh=%.3f kh=%d\n”,sh,kh);
}
4.Аргументом функции является двухмерный массив (матрица)
Описание функции.Такой аргумент можно описать 3-мя способами:
1) как массив с заданным количеством строк и столбцов
void fun(float c[5][10],…)
Внутри функции элементы массива обозначаются: c[i][j]
Недостаток этого способа – функция может работать только с матрицами 5*10
2) как массив с неопределенным количеством строк и заданным количеством столбцов
void fun(float c[][10], int m,…)
где m – количество строк
Внутри функции элементы массива обозначаются: c[i][j]
Достоинство – функция может работать с матрицами, имеющими разное количество строк: A[5][10], B[8][10], C[20][10].
Недостаток – длина строки (2-й параметр) фиксирована – 10.
3) как указатель на начало массива:
void fun(float *ptc, int m, int n,…)
где m – количество строк
n – количество столбцов
Элементы массива: *(ptc+i*n+j) à c[i][j]
Этот способ является универсальным, так как функция может работать с массивами любых размеров.
Вызов функции. При вызове функции первым аргументом указывают имя массива для 1-3 способов, а далее – другие аргументы в зависимости от способа:
1) float d[5][10], v[5][10];
fun (d,…); fun(v,…);
2) float a[7][10], b[12][10];
fun(a, 7,…); fun(b, 12,…);
3) float s[4][6], u[9][7];
fun(s, 4, 6,…); fun(u, 9, 7,…);
Пример для 4-го варианта. Заданы три матрицы A[3,4], B[5,6], C[4,7], элементы которых вычисляются по формулам:
a[i,j]=1.9ij2-3.7i2j
b[k,l]=2.5kl2-4.1k2l
c[i,j]=5.3ij2+3.1i2j
Для каждой из матриц определить сумму элементов строки и произведение элементов столбца, на пересечении которых находится максимальный элемент.
Решение задачи. Необходимо использовать функцию, которая будет выполнять следующие действия:
1) формировать матрицу X[M,N] по формуле:
X[i,j]=fij2+gi2j
2) определять Хmax и его индексы imax, jmax
3) a) вычислять сумму элементов строки imax
б) вычислять произведение элементов столбца jmax
Программа примера для 4-го варианта:
#include<stdio.h>
#include<conio.h>
/* Описание функции fmatr */
void fmatr(float *ptx, int m, int n, float f, float g, float *pt_sx, float *pt_px, char name)
{
int i,j,imax,jmax;
float xmax,sx,px;
printf(“Матрица %c размером %dx%d\n”,name,m,n);
for(i=0;i<=m-1;i++)
{
for(j=0;j<=n-1;j++)
{
*(ptx+i*n+j)=f*i*j*j+g*i*i*j;
printf(“%.1f “, *(ptx+i*n+j));
}
printf(“\n”);
}
imax=jmax=0;
xmax=*ptx;
for(i=0;i<=m-1;i++)
for(j=0;j<=n-1;j++)
if(*(ptx+i*n+j)>xmax)
{
xmax=*(ptx+i*n+j);
imax=i;
jmax=j;
}
printf(“%cmax=%.1f imax=%d jmax=%d\m”, name, xmax, imax, jmax);
sx=0;
for(j=0;j<=n-1;j++)
sx+=*(ptx+imax*n+j);
px=1;
for(i=0;i<=m-1;i++)
p*=*(ptx+i*n+jmax);
*pt_sx=sx;
*pt_px=px;
}
/* Главная функция */
void main()
{
float a[3][4], b[5][6], c[4][7], sa, sb, sc, pa, pb, pc;
clrscr();
fmatr(a, 3, 4, 1.9, -3.7, &sa, &pa, ’A’);
printf(“sa=%.1f pa=%.1f\n”, sa, pa);
fmatr(b, 5, 6, 2.5, 4.1, &sb, &pb, ’B’);
printf(“sb=%.1f pb=%.1f\n”, sb, pb);
fmatr(c, 4, 7, 5.3, 3.1, &sc, &pc, ’C’);
printf(“sc=%.1f pc=%.1f\n”,sc,pc);
}
5. Аргументом функции является другая функция
Описание функции. Такой аргумент описывается как указатель на функцию
void f1(тип (*fun)(тип1 арг1,…),…);
Внутри функции записывается вызов этой другой функции:
fun(x, y, z,…);
При вызове функции указывается только имя конкретной функции-аргумента:
f1(f2,…)
где f2 – имя конкретной функции. Функция f2 должна иметь одинаковый список аргументов с функцией fun.
Пример:. Составить программу для вычисления и вывода на экран значений функций:
f1=1.1*x*sin(2.9x) для –5<=x<=5, Dx=0.2
f2=1.5ecos(1.8y) для 0<=y<=4, Dy=0.1
f3=3.7tg(4.2z) для 1<=z<=6, Dz=0.05
Обозначим: xmin=xn, xmax=xk, Dx=dx
Программа для 5-го варианта:
#include<stdio.h>
#include<math.h>
#include<conio.h>
/* Описание функций f1, f2, f3 */
float f1(float x)
{
return(1.1*x*sin(2.9*x));
}
float f2(float y)
{
return(1.5*exp(cos(1.8*y)));
}
float f3(float z)
{
return(3.7*tan(4.2*z));
}
/* Описание функции ff, которая вычисляет и выводит
значения любой из функций f1, f2, f3 */
void ff(float (*fun)(float x), float xn, float xk, float dx, char fname)
{
float x1,f;
printf(“Значения функции f%c для x Є[%.1f, %.1f] dx=%.2f\n”, fname, xn, xk, dx);
for(x1=xn; x1<=xk; x1+=dx)
{
f=fun(x1);
printf(“%.3f “,f);
}
printf(“\n”);
}
/* Главная функция */
void main()
{
clrscr();
ff(f1, -5, 5, 0.2, ’1’);
ff(f2, 0, 4, 0.1, ’2’);
ff(f3, 1, 6, 0.05, ’3’);
}
7. Работа со строками
В языке С есть тип данных char, который можно использовать как для работы с отдельными символами, так и со строками.
Символ – один символ, заключенный в апострофы: 'A', '!', '+', занимает 1 байт памяти
Строка – один и более символов, заключенных в кавычки "…". При записи в памяти, в конце строки автоматически записывается специальный символ конца строки '\0' – нуль-символ.
"А" – строка в памяти занимает 2 байта: 'A'+'\0'
"ABCDEF" ("ABCDEF\0")
"Results of calculations" В памяти: "Results of calculations\0"
Способы описания строк в программе
1. Строковая константа. Рассмотрена ранее. Используется в операторах ввода-вывода:
printf("Введите значение А=");
scanf("%f",&a);
2. Строка записывается в виде символьного массива:
char mst[50];
char a[30]=”Ошибка ввода данных”;
/* Ввод строки */
scanf(”%s”,mst); //Ввод с клавиатуры строки в массив mst
Со строкой можно работать в целом, используя ее имя – mst или посимвольно:
a[3]==’б’
a[5]==’a’
С символьным массивом можно работать только по одному символу:
char symb[7]={’a’,’b’,’c’,’d’,’e’,’f’,’g’};
symb[2]==’c’
symb= =&symb[0]
3. Строка записывается как указатель на тип char
char *pstr=”Memory size”;
pstr – имя строки
*(pstr+i) – i-й символ
*(pstr+4)==’r’
4. Массив символьных строк
char arr_str[5][40] ={“хлеб”,
”молоко”,
”кар т офель”,
”помидоры”,
”яблоки”};
arr_str[i] – имя i-й строки
arr_str[1] – имя строки "молоко"
arr_str[i][j] – j-й символ в i-й строке
arr_str[2][3]= =’т’
5. Массив указателей на тип char
char *pstr_arr[5]={“клубника”,
“вишня”,
“банан”,
“ананас”,
“слива” };
pstr_arr[i] – имя i-й строки
*(pstr_arr[i]+j) – j-й символ в i-й строке
*(pstr_arr[2]+2)==”н”
Здесь память для строк выделяется по их длине.
Ввод-вывод строк
1. Для ввода строк можно использовать следующие 2 функции:
- scanf(“%s”,имя_строки); - универсальная функция ввода
- gets(имя_строки); - специальная функция для
ввода строк с клавиатуры
Различие между scanf и gets состоит в том, что scanf вводит строку до 1-го пробела, в то время как gets вводит строку полностью, до нажатия клавиши ENTER.
Пример:
char fio[30];
scanf(%s”,fio);
gets(fio);
Вводим с клавиатуры:
Калашников И.П.ENTER
В результате работы функции scanf() в переменную fio запишется только фамилия (строка до первого пробела):
“Калашников”
В результате работы функции gets() в переменную fio запишется вся введенная с клавиатуры строка (до нажатия ENTER):
“Калашников И.П.”
2. Для вывода строк на экран можно использовать 2 функции:
- printf() c форматом %s
printf(“%s”,fio);
- puts(имя_строки);
puts(fio);
Эти функции выводят в строку полностью, но puts() всегда выводит курсор на начало следующей строки, а printf() переводит, если есть символ ‘\n’: “%s\n”.