Параметры функции main()
В соответствии с синтаксисом языка Си основная функция каждой программы может иметь такой заголовок:
int main(int argc, char* argv[], char* envp[])
где: параметр argv – массив указателей на строки; argc – параметр типа int, значение которого определяет размер массива argv, то есть количество его элементов, envp – параметр массив указателей на символьные строки, каждая из которых содержит описание одной из переменных среды(окружения). Под средой понимается та программа (обычно это операционная система), которая «запустила» на выполнение функцию main().
Назначение параметров функции main() – обеспечить связь выполняемой программы с операционной системой, точнее, с командной строкой, из которой запускается программа и в которую можно вносить данные и тем самым передавать исполняемой программе любую информацию. Если внутри функции main() нет необходимости обращаться к информации из командной строки, то параметры обычно опускаются.
Если программист «запускает» программу на языке Си из интегрированной среды разработки, то он редко использует командную строку и должен предпринимать специальные меры, чтобы увидеть эту строку или внести в нее нужную информацию (пункт меню – RUN, подпункт – ARGUMENTS). При запуске программы командная строка явно доступна, и именно в ней записывается имя выполняемой программы. Вслед за именем программы можно разместить нужное количество слов, разделяя их пробелами. Каждое слово из командной строки становится строкой-значением, на которую указывает очередной элемент параметра argv[i], где 0<i<argc. Как и в каждом массиве, в массиве argv[] индексация элементов начинается с нуля, то есть всегда имеется элементargv[0]. Этот элемент является указателем на полное название запускаемой программы. Например, если из командной строки выполняется обращение к программе PR1 из каталога Programs, размещенного на диске С, то вызов MS DOS выглядит так:
С:ProgramsPR1.exe
В главной программе разрешено использовать и третий параметр envp[]. Его назначение – передать в программу всю информацию об окружении, в котором выполняется программа.
TMP=C:WINDOWSTEMP и так далее
В приведенном результате имя переменной связано с именем каталога для временных данных. Изменив значение переменной TMP, можно заставить программу использовать для хранения временных данных другой каталог.
Пример [программа pr_27, pr_28]:
#include<stdio.h>
#include<conio.h>
int main(int argc, char *argv[], char *envp[])
{
int i;
clrscr();
for(i=0; i<argc; i++)
printf("argc = %d argv[%d] -> %s
", argc, i, argv[i]);
getch();
clrscr();
for(i=0; envp[i]; i++)
printf("%s
", envp[i]);
getch();
return 0;
}
Указатели на функции
До сих пор мы рассматривали функцию как минимальный модуль программы, обмен данными с которым происходит через набор параметров функции и с помощью значений, возвращаемых функцией в точку вызова. Теперь перейдем к вопросу о том, почему в языке Си функция введена как один из производных типов. Необходимость в таком типе связана с задачами, в которых функция или ее адрес должна выступать в качестве параметра другой функции или в качестве значения, возвращаемого другой функцией.
В соответствии с синтаксисом указатель на функцию – это выражение или переменная, используемые для представления адреса функции. По определению, указатель на функцию содержит адрес первого байта или первого слова выполняемого кода функции. Важно: арифметические операции над указателями на функции запрещены.
Указатель на функцию как переменная вводится отдельно от определения и прототипа какой-либо функции. Для этих целей используется конструкция:
тип (* имя_указателя) (спецификация_параметров);
где
тип – определяет тип возвращаемого функцией значения,
имя_указателя – идентификатор, произвольно выбранный программистом,
спецификация_параметров – определяет состав и типы параметров функции.
Важнейшим элементом в определении указателя на функцию являются круглые скобки - int (*point) (void). Если записать int *point (void), то это будет не определением указателя, а прототипом функции без параметров с именем point, возвращающей значение типа int*, то есть адрес некоторой переменной типа int.
В отличие от имени функции указатель point является переменной, то есть ему можно присваивать значения других указателей, определяющих адреса функций программы. Принципиальное требование – тип указателя-переменной должен полностью соответствовать типу функции, адрес которой ему присваивается.
Имя функции в ее определении и прототипе – указатель-константа. Он всегда связан с определяемой функцией и не может быть настроен на что-либо иное, чем ее адрес.
Неконстантный указатель на функцию, настроенный на адрес конкретной функции, может быть использован для вызова этой функции. Общий вид вызова функции с помощью неконстантных указателей:
(* имя_указателя) (список_фактических_параметров);
или
имя_указателя (список_фактических_параметров);
в обоих случаях тип указателя должен соответствовать типу вызываемой функции, между формальными и фактическими параметрами также должно быть полное соответствие. При использовании явного разыменования обязательны круглые скобки. Запись *point(); будет ошибочной, так как операция круглые скобки ‘()’имеет более высокий приоритет, чем операция разыменования ‘*’. В этом ошибочном вызове вначале выполниться вызов функции point(), а уже к результату будет применена операция разыменования.
При определении указателя на функции он может быть инициализирован. В качестве инициализирующего выражения должен использоваться адрес функции того же типа, что и тип определяемого указателя:
Пример:
int fnkt(float); // прототип функции
int (*pfnkt1)(float) = fnkt; // pfnkt1 – указатель на функцию fnkt
int (*pfnkt2)(float) = NULL; // pfnkt2 – указатель на функцию, обнуленный
Пример [программа pr_29]:
#include<stdio.h>
#include<conio.h>
void f1(void);
void f2(void);
int main(void)
{
clrscr();
// определяем указатель-переменную с именем point на
// функции без параметров, не возвращающие значение.
void (*point) (void);
// явный вызов функции f1()
f1();
// настройка указателя на функцию f1()
point = f1;
// вызов функции f1() по ее адресу с разыменованием указателя
(*point)();
// настройка указателя на функцию f2()
point = f2;
// вызов функции f2() по ее адресу с разыменованием указателя
(*point)();
// вызов функции f2() по ее адресу без разыменования указателя
point();
getch();
return 0;
}
void f1(void)
{
printf("Function f1()
");
}
void f2(void)
{
printf("Function f2()
");
}
Результат:
Function f1()
Function f1()
Function f2()
Function f2()
Указатели на функции как параметры позволяют создавать функции, реализующие тот или иной метод обработки другой функции, которая заранее не определена. Например, можно определить функцию для вычисления интеграла. Подынтегральная функция может быть передана в функцию вычисления интеграла с помощью параметра-указателя.
Для этого введем формулы численного интегрирования.
Пусть на отрезке [a, b] задана непрерывная функция y=f(x). Требуется вычислить определенный интеграл. Разделим отрезок [a, b] точками a=x0, x1, x2, …, xn=b, на n равных частей длины Dx: Dx=(b-a)/n. Обозначим далее через y0, y1, y2, …, yn значения функции f(x) в точках x0, x1, x2, …, xn, т. е. y0=f(x0), y1=f(x1), y2=f(x2), …,yn=f(xn). Значение интеграла определяется по одной из формул:
Пример [программа pr_30]:
#include<stdio.h>
#include<conio.h>
#include<math.h>
// прототипы функций, используемых в программе
float f1(float);
float f2(float);
float rect(float (*)(float), float, float);
int main(void)
{
clrscr();
// вызов функции rect() для вычисления первого интеграла методом прямоугольников
printf("1) First integral: %f
", rect(f1, -1, 2));
// вызов функции rect() для вычисления второго интеграла методом прямоугольников
printf("2) Second integral: %f
", rect(f2, 0, 0.5));
getch();
return 0;
}
// параметры функции rect() – указатель на функцию, с параметром типа float, возвращающий
// значение типа float; и два параметра типа float для задания пределов интегрирования.
float rect(float (*f)(float), float a, float b)
{
int i;
float s=0, h=(b-a)/N;
for(i=0;i<20;i++)
s+=f(a+i*h);
return h*s;
}
float f1(float x)
{
return x/((x*x+1)*(x*x+1));
}
float f2(float x)
{
return 4*cos(x)*cos(x);
}
При организации меню часто используется указатель на функцию как возвращаемое функцией значение.
int (*menu_items[])(void) = {f1, f2}; - массив указателей на функции, в качестве инициализирующих значений в списке использованы имена функций f1() и f2().
t = (*menu_items[0])();
t = (*menu_items[1])();
Если функция f1() возвращает значение равное 1, то t = 1.
Если функция f2() возвращает значение равное 2, то t = 2.
|