Тип имя(спецификация явных параметров,...);
Последняя запятая необязательна. Многоточие сообщает компилятору, что не нужно проводить дальнейший контроль соответствия количества параметров и их типов при обработке вызова. Переход в списке от параметра к параметру осуществляется с помощью указателя, тип которого является типом переменной. При увеличении указателя на единицу осуществляется переход к следующей переменной такого же типа. В ВС31 функции с переменным числом параметров не работают с типамиchar, unsigned char, float.
В таких функциях существуют два способа определения количества параметров: 1) передача в функцию в списке фактических параметров информации об их количестве; 2) добавление параметра–индикатора в конец списка фактических параметров.
Пример. В функцию передается в списке явных параметров количество элементов, которые суммируются.
#include <stdio.h>
int sumi(int i,...)//Определение функции
{int *num=&i; //1-й параметр – число переменных в списке
int sum=0;
for(;i;i--) sum+=*(++num);//вычисляем сумму со 2-го элемента
return sum;}
double sumd(int i,double d...)//Определение функции
{double *ptr=&d; //указатель на 2-й параметр типаdouble
double sum=0.0;
for(;i;i--) //1-й параметр – число переменных в списке
sum+=*(ptr++);
return sum;}
Void main()
{int a1=10,a2=20,a3=30,s1,s2;
s1=sumi(3,a1,a2,a3); //сумма 3-х чиселa1,a2,a3
s2=sumi(6,1,a1,3,a2,a3,4); //сумма 6-и чисел
double d1=5.7, d2=1.3,s3;
s3=sumd(4,d1,1.25,d2,2.5); //сумма 4-х чисел
printf("s1=%d s2=%d s3=%.2lf\n",s1,s2,s3);}
В результате работы программы на экран выведется:
s1=60 s2=68 s3=10.75
Пример. В функции вычисляется произведение элементов, при этом каждый элемент сравнивается с параметром–индикатором, который находится в конце списка явных параметров.
#include <stdio.h>
#define term 0.0
double mult(double arg...) //Определение функции
{double a=1.0, *ptr=&arg;
if(*ptr==0.0) return 0.0;
for(;*ptr!=term;ptr++)
a*=*ptr;
return a;}
Void main()
{double a=2.5,b=2.0,md1,md2;
md1=mult(a,b,0.3,term);
md2=mult(0.5,a,4.0,b,1.1,term);
printf("md1=%.2lf md2=%.2lf\n",md1,md2);}
В результате работы программы на экран выведется:
md1=1.50 md2=11.00
При работе с функциями, имеющими переменное параметров, имеется набор макроопределений, помещенных в заголовочном файле stdarg.h:
Макрокомандаvoid va_start (va_list param, последний явный параметр)связываетвспомогательный параметрparam с последним явным параметром в списке.
Макрокоманда type va_arg (va_list param,type) перемещает указательparamна число байтзаданного типа type.
Макрокоманда va_end (va_list param) вызывается после обработки списка и организует корректный возврат из функции (вместо оператора return).
1.6 Передача параметров в функцию main()
В функцию main(),как и в любую другую функцию, могут передаваться параметры. Для передачи параметров в функциюmain() ее следует описать в таких формах:
[тип] main(int argc, char **argv){...}
[тип] main(int argc, char **argv, char **envp){...}
[тип] – тип функции mainможет быть void илиint.
Параметр int argc определяет число слов в командной строке.
Параметр char **argv ‑ указатель на массив строк, вводимых из командной строки. Массив строк –это массив указателей, являющихся адресами первых символов этих строк. Размер этого массива строк равен argc. Нулевое слово, на которое указывает указатель argv[0], всегда является спецификацией запускаемой на выполнение программы (именем программы с расширением .exe).
Параметрchar **envp ‑ это указатель на массив указателей на строки среды программы. С помощью этого параметра в программу можно передать всю информацию среде окружения программы, которая определяет некоторые особенности операционной системы и др.
В следующих примерах демонстрируется передача параметров в функциюmain.Для работы программ их необходимо запустить из командной строки, например в среде FAR, NC, VC, DN и др.
Пример.
#include <stdio.h>
#include <conio.h>
int main(int argc,char**argv,char**envp)
{clrscr();
for(int i=0;i<argc;i++)
printf("argv[%d]=%s\n",i,argv[i]);
Getch();
puts("Среда окружения");
while(*envp) puts(*envp++);
Getch();
return 0;}
Пусть исполняемый файл этой программы prog1.exe расположен в корневом каталоге диска C:\ . Тогда, если в командной строке набрать c:\>prog1.exe my age 19, то результат работы программы может быть такой:
argv[0]=C:\PROG1.EXE
argv[1]=my
argv[2]=age
argv[3]=19
Среда окружения
PROMPT=$p$g
COMSPEC=C:\WINDOWS\COMMAND.COM
PATH=C:\BC31\BIN
Proccessor_ArcHitecture=x86
Proccessor_identifier=x86 Family 15 Model 47
Proccessor_Level=15
...
В следующей программе для ее выполнения необходимо набрать заданное слово в командной строке.
Пример.
#include <stdio.h>
#include <conio.h>
#include <string.h>
int main(int argc,char **argv)
{clrscr();
char str[]="Secret";
if(strcmp(argv[1],str)!=0)//сравнение 2-х строк
{printf("Error\n"); getch(); return 1;}
printf("Programma ");
printf("%s %c.%c.\n",argv[2],argv[3][0],argv[4][0]);
int x, a=5, b=7,res1;
sscanf(argv[5],"%d",&x); //преобразование строки в целое число
res1=(a+b)*x; //(5+7)*100
printf("res1=%d\n",res1);
Getch();
return 0;}
Пусть исполняемый файл этой программы prog2.exe расположен в корневом каталоге диска C:\.Для правильной работы этой программы в командной строке надо набрать вторым словом Secret. Если в командной строке набрать:
c:\>prog1.exe Secret Petrov Ivan Ivanovich 100:
то на экран выведется:
Programma Petrov I.I.
res1=1200
Если второе слово в командной строке будет другим, то на экран выведется Error,и программа завершится.
1.7 Рекурсивные и подставляемые функции
Рекурсивная функция – эта такая функция, которая содержит в себе вызов этой же функции, т.е. самовызывающая функция.
Рекурсивные функции желательно не использовать, если есть очевидное итерационное решение.
Пример. Вычисление факториала неотрицательного целого числа
long fact(int k) //Определение функции
{if(k<0) return 0;
if (k==0} return 1;
return k* fact(k-1);}
Void main()
{int n=5; long F_n=fact(n);
printf (“%d!=%ld”,n,F_n);}
На экран выведется: 5!=120
Функция прерывается приk==0,последнее выражение(1*fact(1-1)) ,вычисляет k*(k-1)*(k-2)*(k-3)*(k-4)*1.
Подставляемая функция(inline-функция) – это функция, тело которой подставляется при каждом ее вызове. Такая функция определяется с помощью слова inline.
Inline-функция обрабатывается компилятором как встраиваемая, т.е. вместо каждого вызова этой функции компилятор попытается подставить тело функции. При многократных вызовах код программы может значительно увеличиться, но скорость работы программы повысится за счет отсутствия вызовов этой функции.
Пример.
inline float mod(float x,float y)//Определение функции
{return sgrt(x*x+y*y);}
Void main()
{float f1=4.5, f2=-2.5, f3=-1.5,res1,res2;
res1=mod(f1,f2); // 5.1478
//res1=sgrt(f1*f1+f2*f2);
res2=mod(f1,f3); } // 4.743
//res2=sgrt(f1*f1+f3*f3); }
В ВС31 для inline-функции принято: 1) функция не должна быть рекурсивной; 2) функция не должна содержать операторов for, while, do, switch, goto. В этих случаях компилятор выдает предупреждение, и функция будет трактоваться как обычная.
Пример. Функция вводит символы, пока не введется ’q’. Функция рекурсивная, поэтому не может быть реализована как inline-функция.
inline void str() //Функции будет определена как обычная
{char b; if(cin>>b&&b!='q') str();}
1.8 Функции, возвращающие указатель
Если функция возвращает указатель, то в прототипе и в определении функции в качестве возвращаемого типа записывается тип возвращаемого указателя.
Пример. Функция возвращает адрес элемента массива, значение которого равно нулю. В программе это значение заменяется на 1.
#include <stdio.h>
int *func(int ,int []); //прототип функции
Void main()
{const N=5;
int *b,arr[N]={3,0,7,0,9}, n=0;
if((b=func(N,arr))!=NULL) //вызов функции
{n++; *b=1;
printf("*b=%d b=%p\n",*b,b);}
printf("выполнено замен %d \n",n);}
int *func(int n,int a[]) //Определение функции
{for (int i=0;i<n;i++)
if(a[i]==0) return &a[i];
return NULL;}
На экран выведется:
*b=1 b=8ff2:0FF2
*b=1 b=8ff2:0FF8
выполнено замен =2
1.9 Функции и структуры
В функции можно передавать и из функций можно возвращать структуры и объединения. В этом случае в качестве возвращаемых типов и в сигнатуре функции записываются структурные типы или типы объединений. При передаче структуры в функцию, в функции создается копия этой структуры, и действия, производимые со структурой внутри функции, не влияют на структуру в вызывающей функции. Если в функцию передается указатель на структуру, то функция будет обрабатывать непосредственно элементы той структуры, на которую в функцию передан указатель.
Пример. В функцию передается структура и указатель на структуру
#include <string.h>
#include <stdio.h>
struct STUD{char name[20];int age;};
Void f1(STUD a)
{strcpy(a.name,"Петров"); //Копируем вa.name "Петров"
a.age+=1;
printf("%s %d\n",a.name,a.age); } // Петров 20
STUD f2(STUD b,STUD с)
{if(b.age>с.age) return b;
//возвращаем ту структуру, в которой age больше
return с;}
void f3(STUD *p)
{strcpy(p->name,"Сидоров");
p->age=21;
printf("%s %d\n", p->name, p->age);} // Сидоров 21
STUD *f4(STUD *r1,STUD *r2)
{if(r1->age>r2->age) return r1;
//возвращаем указатель на ту структуру, в которойageбольше
return r2;}
Void main()
{STUD st1={"Иванов",19},st2={"Орлов",22},st3;
F1(st1);
printf("%s %d\n",st1.name,st1.age);//все равноИванов 19
st3=f2(st1,st2);
printf("%s %d\n",st3.name,st3.age); // Орлов 22
STUD st4={"Сергеев",18},st5={"Алешин",25},*pst;
f3(&st4);
printf("%s %d\n",st4.name,st4.age); // Сидоров 21
pst=f4(&st4,&st5);
printf("%s %d\n",pst->name,pst->age);} // Алешин 25
1.10 Перегрузка функций
Цель перегрузки функций состоит в том, чтобы функция с общим именем возвращала разные значения при обращении к ней с разными по типам и количеству фактическими параметрами.
Перегруженные функции – это функции, которые имеют одинаковое имя, но различаются по типам и по количеству передаваемых в них параметров.
Распознавание перегруженных функций при вызове выполняется только по сигнатурам (списку формальных параметров функции). Для обеспечения перегрузки необходимо для каждого имени определить, сколько разных функций связано с ним.
Пример. Функции различаются по типу передаваемых в них параметров
#include <stdio.h>
int FF(int x,int y) {return x*x+y*y;}
long FF(long x, long y) {return x*x+y*y;}
float FF(float x, float y) {return x*x+y*y;}
Void main()
{int a1=3,a2=4,a3; long b1=20,b2=400,b3;
float d1=5.5,d2=7.5,d3;
a3=FF(a1,a2);
b3=FF(b1,b2);
d3=FF(d1,d2);
printf("a3=%d b3=%ld d3=%.2f\n", a3,b3,d3);}
В результате работы программы на экран выведется:
a3=25 b3=160400 d3=86.50
Пример. Функции различаются по количеству передаваемых в них параметров.
#include <stdio.h>
double mul(double x){return x*x*x;}
double mul(double x,double y){return x*y*y;}
double mul(double x,double y,double z){return x*y*z;}
Void main()
{double M1=mul(0.2);
double M2=mul(0.2,0.4);
double M3=mul(0.2,0.3,0.4);
printf("M1=%.4lf M2=%.4lf M3=%.4lf\n", M1,M2,M3);}
В результате работы программы на экран выведется:
M1=0.0080 M2=0.0322 M3=0.0240
При определении функции с одинаковой сигнатурой, но с другим телом и типом возвращаемого значения, компилятор сообщит об ошибке, т.к. функция с такой сигнатурой уже определена. Например, если в данном примере доопределить следующую функцию, то появится сообщение об ошибке: