2842

Функции как совокупность объявлений и операторов

Контрольная

Информатика, кибернетика и программирование

Функции Функция – это совокупность объявлений и операторов, предназначенных для выполнения отдельной задачи и заключённых в специальный блок. Необходимость в использовании функций возникает при решении сложных задач, когда нужно выполнять набор...

Русский

2012-10-20

79 KB

2 чел.

Функции

Функция – это совокупность объявлений и операторов, предназначенных для выполнения отдельной задачи и заключённых в специальный блок. Необходимость в использовании функций возникает при решении сложных задач, когда нужно выполнять набор однотипных действий с различными данными. Любая программа на языке C должна содержать хотя бы одну функцию (функция main()). С использованием функций в языке C связаны три основные понятия: объявление, определение и вызов.

Определение функции – это описание действий, выполняемых функцией.

Форма записи:

модификатор1 тип модификатор2 имя (список_формальных_параметров)

{

тело_функции

}

Поле модификатор1 содержит спецификации класса памяти и является необязательным. Поле модификатор2 используется только в средах разработки фирмы Borland для изменения типа функции (cdecl или pascal) и является необязательным. Подробнее об этом – при рассмотрении классов памяти и модификаторов. Поле тип является обязательным и определяет тип возвращаемого функцией в вызывающую функцию значения. Если тип не указан явно, то по умолчанию считается, что функция возвращает значение типа int. Если функция не возвращает никакого значения, она должна иметь тип возвращаемого значения void. Функция может возвращать переменную, константу или указатель, но не может возвращать массив или другую функцию, для этого используются указатели. Поле имя является обязательным и определяет идентификатор функции. Поле список_формальных_параметров содержит объявления параметров функции, указанные через запятую, которые могут быть использованы в теле функции. Если функция не имеет параметров, то в данном поле указывается слово void. Формальные параметры – это локальные объекты (переменные, константы, структуры, объединения или указатели), существующие только в пределах тела функции и принимающие значения, переданные функции при вызове в соответствии с порядком следования их имён в списке параметров. Синтаксические правила объявления формальных параметров функции аналогичны синтаксическим правилам объявления соответствующих объектов. Тип формальных параметров может быть любым, но типы формальных параметров, указанных в определении функции, должны соответствовать типам формальных параметров, указанных в объявлении функци, и типам фактических параметров, передаваемых в тело функции при её вызове. Поле тело_функции является обязательным и задает последовательность операторов, выполняемых функцией.

Пример 1

void printf_bigger(int a,int b)

{

int c=a;

if(c<b)

 c=b;

printf("%d",c);

}

При компиляции программы компилятор последовательно читает исходный код программы. Появление идентификатора функции (операция «вызов функции») в тексте программы приводит к генерации компилятором последовательности команд, обеспечивающей вызов функции (занесение в стек текущих параметров выполняемой программы, значений фактических параметров, передаваемых функции, и передачу управления на начальную команду тела функции). Чтобы функция могла быть вызвана, она должна быть объявлена или определена, поэтому объявление или определение функции должно всегда предшествовать ее вызову. Если определение функции находится в другом модуле или в том же модуле ниже, чем её вызов, для информирования компилятора может использоваться объявление функции, которое должно находиться раньше, чем вызов функции в этом модуле.

Объявление функции (задание прототипа функции) – это задание формы обращения к функции.

Форма записи:

модификатор1 тип модификатор2 имя (список_формальных_параметров);

Смысл полей модификатор1, модификатор2, тип и имя такой же, как и при определении функции. Поле список_формальных_параметров определяет количество и типы передаваемых функции параметров. В данном списке при объявлении функции достаточно указать только тип параметра, идентификатор параметра задавать не обязательно. Если всё же в объявлении функции используются идентификаторы параметров, то они не обязательно должны совпадать с идентификаторами параметров при определении функции, но строго должны совпадать их типы.

Пример 2

void printf_bigger(int,int h);

void main(void)

{

int i=2;

printf_bigger(i+1,3);

}

void printf_bigger(int a,int b)

{

int c=a;

if(c<b)

 c=b;

printf("%d",c);

}

Определение функции будет одновременно являться и объявлением функции, если функция определяется до того, как будет вызвана.

Пример 3

void printf_bigger(int a,int b)

{

int c=a;

if(c<b)

 c=b;

printf("%d",c);

}

void main(void)

{

int i=2;

printf_bigger(i+1,3);

}

Вызов функции – это операция передачи управления на первый оператор тела функции.

Форма записи:

операнд1(операнд2)

Поле операнд1 содержит адрес функции. Это либо указатель на функцию, либо идентификатор функции (который по своей природе также является указателем на функцию), либо выражение, тип значения которого – указатель на функцию. Поле операнд2 содержит список фактических параметров функции. Это один или несколько, разделенных запятой, объектов, доступных в вызывающей функции, которые имеют конкретные значения, передаваемые функции при вызове. Их отличие от формальных параметров в том, что последние не содержат конкретных значений и при каждом вызове функции принимают значения фактических параметров вызова.

Если функция возвращает какое-либо значение, то вызов функции может входить в другое выражение.

Вызов функции приводит к следующим действиям:

  •  вычисляется значение полей операнд1 (адрес функции) и операнд2, если типы фактических параметров не совпадают с типами формальных параметров в объявлении и определении функции, то производится неявное преобразование типов фактических параметров к типам формальных параметров, соответствие между фактическими и формальными параметрами задаётся порядком их следования в списке;
  •  в стек копируется адрес возврата из функции (адрес следующей за вызовом функции команды);
  •  значения фактических параметров копируются в ячейки памяти, предназначенные для хранения параметров функции (в стек);
  •  управление передаётся по адресу функции, который является первым оператором тела функции;
  •  последовательно выполняются операторы, составляющие тело функции, выполнение оператора return в теле функции возвращает управление в вызывающую функцию, при отсутствии оператора return управление возвращается после выполнения последнего оператора тела функции;
  •  при возврате из функции производится очистка стека: удаляются фактические параметры и адрес возврата, по которому передаётся управление в вызывающую функцию, если функция возвращает значение оператором return, то производится возврат значения в точку вызова;
  •  управление передаётся на следующую за вызовом функции команду.

Пример 4

printf_bigger(i+1,3)

Для возврата из функции используется оператор «return». В теле функции может быть несколько операторов «return». Форма записи:

return оператор

Оператор может быть пустым оператором или оператором «выражение».

Если оператор является пустым оператором, то возвращаемое значение функции отсутствует, функция просто передаёт управление в точку вызова. Это допустимо, если функция имеет тип возвращаемого значения void.

Если оператор является оператором «выражение», то возвращаемым значением функции будет значение данного выражения, которое неявно преобразуется к типу возвращаемого значения функции. Возвращаемое значение возвращается в точку вызова функции.

Если оператор «return» отсутствует, то функция работает до выполнения всех операторов, составляющих её тело, после чего управление передаётся в точку вызова. Это допустимо, если функция имеет тип возвращаемого значения void.

Пример 5

void printf_bigger(int a,int b)

{

int c=a;

if(c<b)

 c=b;

return;

printf("%d",c);

}

void main(void)

{

int i=2;

printf_bigger(i+1,3);

}

Программа ничего не напечатает.

Пример 6

int printf_bigger(int a,int b)

{

return a>b?a:b;

}

void main(void)

{

int i=2;

printf("%d",printf_bigger(i,3)+2);

}

На экране будет напечатано: 5

Параметры функции

В языке C существует два способа передачи параметра в функцию: передача по имени и передача по ссылке. Кроме того, функция может в процессе работы изменять значения глобальных переменных программы.

Если используется передача по имени, то при вызове функции в качестве фактического параметра указывается имя переменной, и в стеке выделяется место для формального параметра, куда копируется значение фактического параметра функции. Далее функция работает только со значением параметра в стеке. При выходе из функции стек очищается и данные значения, даже если они были модифицированы функцией, теряются. В языке C  вызванная функция не может изменить значения переменных, указанных в качестве фактических параметров функции, передаваемых по имени при обращении к ней. После выхода из функции значение переменной будет таким, каким оно было до входа в функцию.

Если необходимо, чтобы функция могла изменять значения переменных, переданных ей в качестве фактических параметров, используют передачу параметров в функцию по ссылке. В этом случае в качестве фактического параметра указывается адрес переменной, который и заносится в стек. Вызванная функция обращается с параметром через адрес, находящийся в стеке, тем самым работая с основной переменной, а не с её копией. После выхода из функции значение переменной будет таким, каким оно было при выходе из функции.

Если в качестве параметра функции используется массив, то его можно передать в качестве параметра в функцию только по ссылке, в этом случае передается указатель на первый элемент массива. При указании многомерного массива в качестве фактического параметра функции лучше всего явно указывать его размеры по всем координатам, или передавать указатель на первый элемент массива.

В функцию в качестве параметра может быть передано имя другой функции, которое может быть использовано в процессе выполнения.

Пример 7

void increment(int a, int *c)

{

a++;

(*c)++;

printf("%d %d ",a,*c);

}

void main(void)

{

int i=2,k=8;

increment(i,&k);

printf("%d %d",i,k);

}

На экране будет напечатано: 3 9 2 9

Пример 8

int abs_sum(int a, int b, int modul(int))

{

return modul(a)+modul(b);

}

int absolute(int x)

{

return x>=0?x:-x;

}

void main(void)

{

printf("%d ",abs_sum(-2,5,absolute));

}

На экране будет напечатано: 7

Параметры и возвращаемое значение функции main()

Функция main() может иметь или не иметь параметры и возвращать или не возвращать значение. Если она не имеет параметров или не возвращает значение, то в соответствующем поле указывается слово void.

Функция main() может иметь параметры, фактические значения которых могут быть переданы ей из командной строки или из другой программы при запуске программы на выполнение. В языке C имеются два встроенных параметра функции main(): int argc и char *argv[]. Использование именно таких идентификаторов не обязательно, но обязателен тип и порядок параметров. Эти параметры используются для передачи параметров командной строки. Параметр argc содержит количество параметров командной строки и имеет тип int. Он всегда не меньше единицы, так как имя программы, вызываемой для выполнения, трактуется как первый параметр командной строки. Параметр argv является указателем на массив строк. Каждый элемент массива содержит соответствующий параметр командной строки, один параметр командной строки отделяется от другого пробелом. Если в качестве параметра необходимо иметь строку, содержащую пробел, её необходимо заключить в кавычки. Если параметром командной строки является число, то оно рассматривается как строка и должно быть преобразовано с использованием специальных библиотечных функций.

В среде Borland C++ 3.1 предусмотрен ещё и третий параметр: char *env[]. Этот параметр используется для доступа к параметрам среды операционной системы (переменные окружения) и является указателем на массив строк, которые содержат эти параметры.

Параметры функции main() позиционно зависимы, то есть при использовании какого-либо параметра должны быть объявлены и все предыдущие параметры, но необязательно объявлять последующие параметры.

Функция main() может возвращать в ОС или вызывающую программу значение типа int. Для ОС MS-DOS возврат нулевого значения говорит об успешном завершении, возврат значения, отличного от нуля, говорит об аварийном завершении.

Пример 9

#include <stdio.h>

int main(int argc, char *argv[], char *env[])

{

for(int i=0;i<argc;i++)

 printf("%s\n",argv[i]);

for(int i=0;env[i];i++)

 printf("%s\n",env[i]);

 return 0;

}

Данная программа, запущенная на выполнение в ОС MS-DOS, напечатает на экране командную строку MS-DOS, введённую при её запуске, и список переменных окружения. Если после неё ввести командную строку MS-DOS

if errorlevel 0 exit

то произойдёт перезагрузка ОС.

Функции с переменным числом параметров

В объявлении и определении такой функции переменное число параметров задается многоточием в конце списка формальных параметров, при этом запятая после последнего фиксированного параметра перед многоточием необязательна. Функция с переменным числом параметров должна иметь хотя бы один фиксированный параметр. При вызове функции с переменным числом параметров задается любое требуемое число параметров.

В языке C предусмотрены специальные средства, определённые в заголовочном файле stdarg.h и используемые в самой функции для работы с её параметрами:

тип данных

va_list

макрос

va_start(va_list list, last_fixed)

макрос

arg_type va_arg(va_list list, arg_type)

макрос

va_end(va_list list)

Тип данных va_list по своей сути является указателем типа void и используется специально для доступа к параметрам функции с переменным числом параметров.

Макрос va_start устанавливает параметр list типа va_list на начало списка необязательных параметров, поле last_fixed должно содержать имя последнего обязательного параметра. Макрос va_start должен быть использован до первого использования макроса va_arg.

Макрос va_arg осуществляет последовательный доступ к переменным параметрам функции, извлекая очередной параметр функции по адресу, заданному параметром list, тип данного параметра функции определяется полем arg_type. Тип должен быть таким типом данных, чтобы объект данного типа мог быть передан в качестве параметра функции, проверка соответствия типа не производится, то есть совпадение типа в va_arg при извлечении параметра и типа фактического параметра при вызове функции должно контролироваться программистом. При каждом извлечении нового параметра макросом va_arg указатель list модифицируется в соответствии с типом извлекаемого параметра и указывает на следующий необязательный параметр функции. Возвращаемое макросом значение – извлеченный переменный параметр функции, имеющий тип arg_type.

Макрос va_end завершает работу с параметром list типа va_list и должен быть использован после извлечения всех параметров макросом va_arg. Его следует использовать в целях переносимости программы на другие платформы и компиляторы, хотя никаких особых действий в среде он не производит.

Основная проблема при использовании переменного числа параметров заключается в том, что в функции не известны ни их количество, ни их типы. Для определения этого существуют следующие подходы:

  •  можно использовать подход как в функции printf(), где строка формата является обязательным параметром и полностью определяет количество и типы необязательных параметров через спецификации формата;
  •  можно зарезервировать какое-либо значение необязательного параметра как показатель того, что необязательный параметр с данным значением – последний переданный параметр, но при этом все необязательные параметры будут для всех вызовов иметь один тип;
  •  можно передавать число необязательных параметров в качестве фиксированного параметра, но при этом все необязательные параметры также будут для всех вызовов иметь один тип.

В файле stdio.h определено семейство функций v…printf() и v…scanf(), которые можно использовать внутри функций с переменным числом параметров.

Пример 10

#include <stdio.h>

#include <stdarg.h>

#include <string.h>

double sum1(char *string...)

{

va_list args;

int length=strlen(string);

double result=0;

va_start(args,string);

for(int j=0;j<length;j++)

{

 switch(*(string+j))

 {

  case 'i':

   result+=va_arg(args,int);

   break;

  case 'c':

   result+=va_arg(args,char);

   break;

  case 'f':

   result+=va_arg(args,double);

   break;

  case 'd':

   result+=va_arg(args,double);

   break;

  default:

   va_end(args);

   return 0;

 }

}

va_end(args);

return result;

}

int sum2(int a,...)

{

va_list args;

int result=a,t;

va_start(args,a);

while((t=va_arg(args,int))!=0)

 result+=t;

va_end(args);

return result;

}

int sum3(int num,int a...)

{

va_list args;

int result=a;

va_start(args,a);

for(int i=0;i<num;i++)

 result+=va_arg(args,int);

va_end(args);

return result;

}

void main(void)

{

char c=2;

float f=0.5;

printf("%f %d %d",sum1("cfdidcf",c,f,0.4,1,0.1,c,0.2f),sum2(1,2,3,0),sum3(2,4,5,6));

}

На экране будет напечатано: 6.200000 6 15

Использование типов данных при извлечении зависит от реализации компилятора. В примере 10 в функции sum1 при извлечении числа типа float используется тип double.

Рекурсивные функции

В языке C функции могут вызывать сами себя. Функция называется рекурсивной, если во время ее работы возникает ее повторный вызов, либо непосредственно, либо косвенно, путем цепочки вызовов других функций.

Прямой рекурсией является вызов функции внутри тела этой функции. Косвенной рекурсией является рекурсия, осуществляющая рекурсивный вызов функции посредством цепочки вызова других функций. Все функции, входящие в цепочку, тоже считаются рекурсивными.

Вызов рекурсивной функции создаёт в стеке новые копии локальных переменных и параметров. Из рекурсивной функции необходимо предусмотреть выход. Компилятор допускает любое число рекурсивных вызовов, однако слишком большое число рекурсивных вызовов может вызвать переполнение стека. Наибольшее число рекурсивных вызовов связано с объёмом в байтах передаваемых функции фактических параметров. Теоретически чем меньше объём передаваемых параметров, тем большее число рекурсивных вызовов, не приводящих к переполнению стека, возможно.

Пример 11

int factorial(int n)

{

if(n==1)

 return 1;

return factorial(n-1)*n;

}

void main(void)

{

printf("%d",factorial(8));

}

На экране будет напечатано: 40320

Указатель на функцию

На функцию, как и на любой другой объект, можно создать указатель. Указатель на функцию содержит адрес первого оператора исполняемого кода функции. Форма записи:

модификатор тип (*имя_указателя)(список_формальных_параметров)

Смысл поля модификатор аналогичен смыслу поля модификатор1 при определении функции. Поле тип определяет тип возвращаемого функцией значения. Поле имя_указателя является идентификатором указателя. Поле список_формальных_параметров содержит список формальных параметров функции. Для обращения к функции через указатель на функцию данному указателю должно быть присвоено значение адреса функции. Форма записи:

имя_указателя=имя_функции

или

имя_указателя=&имя_функции

Поле имя_функции содержит имя функции, адрес которой присваивается указателю. Скобки, являющиеся операцией «вызов функции», не ставятся. Содержимое поля список_формальных_параметров в объявлении указателя должно совпадать с содержимым аналогичных полей в объявлении и определении функции.

Вызов функции через указатель на функцию осуществляется через обращение по адресу указателя с указанием в скобках фактических параметров вызова функции. Форма записи:

(*имя_указателя)(список_фактических_параметров)

или

имя_указателя(список_фактических_параметров)

Можно определить массив указателей на функции. Форма записи:

тип (*имя_указателя[размер1][размер2]…[размерN]) (список_формальных_параметров)={список_инициализаторов}

Смысл полей тип, имя_указателя и список_формальных_параметров такой же, как и при объявлении указателя на функцию, смысл полей, определяющих размеры массива, и поля список_инициализаторов такой же, как и при объявлении массивов. При этом возможно использование формы объявления массива указателей на функцию, аналогичной второй форме объявления массива. Недостаток массива указателей на функцию в том, что списки формальных параметров всех функций, на которые указывают указатели массива, должны совпадать.

Пример 12

int sum(int a, int b)

{

return a+b;

}

int square_sum (int (*p)(int,int),int x, int y)

{

int sum=p(x,y);

return sum*sum;

}

void main(void)

{

int (*f)(int,int);

f=sum;

printf("%d %d",(*f)(3,2),square_sum(f,3,4));

}

На экране будет напечатано: 5 49


 

А также другие работы, которые могут Вас заинтересовать

72838. Местообитания и экологическая ниша. Типы связей и взаимоотношений между организмами 80.5 KB
  Для того чтобы описать экологическую нишу организма нужно знать чем он питается кому он служит пищей его способность к передвижению и его воздействие на другие организмы и на неживые элементы внешней среды.
72839. Нормирование ЭМП. Защита от воздействия электромагнитных полей 67 KB
  Различные виды неионизирующих излучений электромагнитных полей ЭМП оказывают разное физиологическое воздействие. В связи со всё большим распространением источников ЭМП в быту СВЧ микроволновые печи мобильные телефоны телерадиовещание и на производстве оборудование ТВЧ радиосвязь...
72840. Техногенные источники электромагнитных полей. Электростатические поля. Биологическое действие электромагнитных полей 62 KB
  Однако за последние 50-60 лет возник и сформировался новый значимый фактор окружающей среды – ЭМП антропогенного происхождения или электромагнитный смог. Его создают 2 большие группы искусственных источников: изделия которые специально создавались для излучения электромагнитной энергии...
72841. Воздействие вредных веществ на организм человека в условиях производства. Системы промышленной вентиляции и кондиционирования 66 KB
  Вредные вещества могут проникать в организм человека через органы дыхания органы пищеварения а также кожу и слизистые оболочки. Через дыхательные пути попадают пары газо и пылеобразные вещества через кожу -– преимущественно жидкие вещества.
72843. Промышленные источники вибраций. Биологическое действие вибраций. Нормирование вибраций 61 KB
  Вибрация - это механическое колебательное движение системы с упругими связями; движение точки или механической системы, при котором происходит поочередное возрастание и убывание во времени значений по крайней мере одной координаты.
72844. Инфразвук. Средства защиты от инфразвука 58.5 KB
  Снижение неблагоприятного воздействия инфразвука достигается комплексом инженерно-технических и медицинских мероприятий основными из которых являются: устранение причин генерации инфразвука в источнике образования повышение жесткости конструкций больших размеров устранение...
72846. Классификация энергетических загрязнений. Естественный фон. Понятие о шумах. Источники шума естественного и техногенного происхождения. Биологическое действие шумов. Нормирование шумов 64 KB
  В окружающую среду поступают энергетические загрязнения в виде шума вибрации электромагнитных долей радиоактивных излучений. Источники: Источники шума в окружающей человека среде могут быть разбиты на две большие группы: внешние и внутренние.