2.12.1. Функции в Си
- int //описание возвращаемого значения
func( //имя функции
int i, //описание первого параметра
int j //описание второго параметра
) //
{ //тело функции
return
i%j; // оператор возврата
} //конец тела функции.
Для функции, которая не возвращает
значения, необходимо записать описатель void. Например:
- void //функция ничего не возвращает
Show( //имя функции
int i, //описание первого параметра
int j //описание второго параметра
) //
{ //тело функции
printf(“%d %d\n”,i,j); // вывод значения i, j
} //конец тела функции, оператор return может
отсутствовать .
Оператор return может быть в любом
месте подпрограммы. Вызов подпрограммы записывается следующим образом:
записывается имя функции и в скобках список выражений для параметров функции,
если они есть. Например:
-
func(10,5);
Show(i+1,k++);
В тех случаях, когда функция
возвращает значение, то вызов функции можно записывать в выражении. Например:
-
x=func(2,3)+20;
Список параметров в функции может
отсутствовать.
Рассмотрим вопросы обмена информацией
между программной и подпрограммой. Возможны следующие варианты:
-
использование параметров;
-
использование общей памяти.
Рассмотрим передачу информации через
параметры. Заранее известно, что значения параметров в функцию передаются по
значению. Это означает, что значение переменной или выражения переписывается в
локальную память функции (причем локальная память выделяется из системного
стека). Поэтому значения переменных, подставленные вместо параметров, не
изменяются. Например:
-
void f(int i) { i=4; }
...
int j=5;
f(j);
В этом примере значение j не
изменится при вызове функции f. Для того чтобы значение переменной изменилось,
необходимо передать адрес переменной, это можно сделать, используя указатели.
Например:
-
void v(int * p) { *p=4; }
...
int k=5;
v(&k);
В этом примере значение переменной k
изменится, поскольку было передано не значение переменной, а ее адрес. Часто
такой подход используют при работе со структурами.
-
typedef struct A { type1 a1; type2 a2; ... } ASTRUCT;
ASTRUCT x, y;
FuncA(ASTRUCT * s) { s->a1=0; s->a2=0; ... }
...
FuncA(&x);
FuncA(&y);
...
Рассмотрим вопросы организации
передачи информации через внешние переменные. Как это организовать?
-
... prog1.c //первый модуль
int x; //это статическая переменная, объявленная вне функции
void func1() { x=100; }
void func2(int i) { x=i; }
...
... prog1.c //второй модуль
extern int x;
void Show() { printf(“%d\n”,x); }
void func3() { return x; }
Файлы prog1.c и prog2.c, которые
имеют внешнюю память x и функции, которые читают и пишут значения в эту внешнюю
память. В первом файле объявляется статическая переменная x, во втором
объявляется, что переменная x описывается как переменная, которая объявлена вне
данного файла.
Есть еще одна важная деталь: порядок занесения значений параметров в стек при вызове функции. Существует
несколько вариантов:
- занесение
с конца списка параметров (cdecl);
- занесение
с начала списка параметров (pascal).
Функция может иметь переменное число
параметров. Для записи функции с переменным числом параметров необходимо
использовать лексему … (три точки). Например:
-
void printf(char * format,...);
Три точки, стоящие вместо описания
параметра, указывают на то, что функция имеет переменное число параметров. Например:
- int sum_vect( int n,...){
int
*vec=&n+1;
int
sum=0;
int
i;
for(i=0;
i<n; i++) sum+=vec[i];
return
sum;
}
Эта функция находит суммы чисел,
передаваемых в качестве аргументов. Первым аргументом является количество
чисел, передаваемых в качестве аргументов. Например:
-
int s=sum_vect(5,20,10,15,1,2); //s=20+10+15+1+2
int count=sum_vect(3,i,j,k); //count=i+j+k
В системном каталоге INCLUDE имеется
заголовочный файл stdarg.h, который содержит описания макросов для выделения
аргумента заданного типа из списка аргументов:
-
void va_start(va_list ap, lastfix);
type va_arg(va_list ap, type);
void va_end(va_list ap);
- где:
-
va_list - это тип указателя на список
аргументов;
ap - указатель на список аргуметов;
lastfix - это имя параметра, стоящего
перед ... (тремя точками);
va_start - инициализация указателя на
список аргументов;
type - тип аргумента;
va_arg - выделение значения
очередного значения аргумента, при этом указатель ap автоматически перемещается
на следующий;
va_end - макрос завершения.
- #include <stdio.h>
#include <stdarg.h>
//вычислить сумму последовательности целых чисел, заканчивающуюся на 0
void sum(char *msg,
...)
{
int
total = 0;
va_list ap;
int
arg;
va_start(ap, msg);
while
((arg = va_arg(ap,int)) != 0) {
total += arg;
}
printf(msg, total);
va_end(ap);
}
int main(void)
{
sum("The total of 1+2+3+4 is
%d\n", 1,2,3,4,0);
return
0;
}
//найти суммы разнотипных чисел, используя строку формата
#include <stdio.h>
#include <stdarg.h>
double sum2(char
*format, ...)
{
double
total = 0;
va_list ap;
int
arg;
va_start(ap, format);
while(*format){
if(*format==’i’)
total+=va_arg(ap,int);
else
if(*format==’d’) total+=va_arg(ap,double);
format++;
}
va_end(ap);
return
total;
}
int main(void)
{
printf("%f",sum2("iddi",
1,2.77,3.5,4));
return
0;
}