4870

Функции. Способы передачи параметров. Значения по умолчанию. Рекурсия

Лекция

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

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

Русский

2012-11-28

46.5 KB

9 чел.

Функции. Способы передачи параметров. Значения по умолчанию. Рекурсия.

Функцией называют «поименованный» блок программного кода. Передача управления этому блоку в процессе работы программе осуществляется в виде вызова функции. Функция может иметь параметры (аргументов), а также возвращаемое значение. В каждой программе на С++ должна присутствовать функция main, которая первой получает управление при запуске программы. Все остальные функции вызываются из main (с учетом возможной вложенности вызовов).

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

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

// Функция вычисляет квадрат заданного числа

double square( double d )

{

 return d * d;

}

// Вызов функции

double x = square( 5.0 );

Оператор return предписывает немедленный выход из функции с возвратом соответствующего значения. Если функция не имеет возвращаемого значения (т.е. имеет тип void), то и return вызывается без параметров. Завершающий return для таких функций можно опустить.

 

// Функция выводит на экран корень числа, переданного в качестве параметра

void printSqrt( double d )

{

 if ( d < 0 )

{

 std::cout << "Ошибка! d < 0" << std::endl;

 return;

}

std::cout << "Корень из d: " << std::sqrt( d ) << std::endl;

}

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

void coutendl() // функция выполняет «перевод строки» на экране

{

std::cout << std::endl;

}

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

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

Различают 3 способа передачи параметров в функцию. При передаче по значению параметр получает копию значения, переданного вызывающим кодом. При этом изменение значения параметра внутри функции никак не затрагивает «оригинальное», внешнее значение:

double x2( double d )

{

 // Изменяется значение локальной копии

d = d * 2;

 return d;

}

// Вызывающий код

double y = 5;

double z = x2( y );

// y не изменилось после вызова функции x2

std::cout << y << std::endl;

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

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

// Функция обменивает значения двух переменных

void swap( int * px, int * py )

{

 // Запомнили значение по адресу px во временную переменную

 int tmp = * px;

 // Заменили значение по адресу px на значение по адресу pу

* px = * py;

 // Заменили значение по адресу py на tmp

* py = tmp;

}

// Вызывающий код

int a = 5, b = 3;

// Передаем адреса переменных

swap( & a, & b );

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

При использовании параметров-ссылок реализация swap будет выглядеть так:

// Функция обменивает значения двух переменных

void swap( int & x, int & y )

{

 // Запомнили значение x во временную переменную

 int tmp = x;

 // Заменили значение x на у

x = y;

 // Заменили значение y на tmp

y = tmp;

}

// Вызывающий код

int a = 5, b = 3;

// Передаем адреса переменных

swap( a, b );

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

Механизм передачи параметров по адресу и по ссылке часто также используют для возврата дополнительных переменных из функции:

// Функция реализует "безопасное" деление.

// Возвращаемое значение - признак "успеха":

// false при делении на 0

// Параметр result используется как выходной параметр

// и получает значение частного

bool divide( double a, double b, double & result )

{

  if ( b == 0 )

     return false;

  

  result = a / b;

  return true;

}

// Вызывающий код:

double a = 4, b = 5;

double c;

if ( ! divide( a, b, c ) )

{

  // ошибка!

}

Значение параметра по умолчанию –  это значение, которое подразумевается подходящим в большинстве предполагаемых сценариев  использования функции. Это позволяет избежать необходимости передавать значения всех параметров функции во многих случаях. Значения по умолчанию для одного или нескольких параметров оформляются подобно инициализации переменных. При вызове функции, фактические аргументы сопоставляются с формальными параметрами последовательно, и значения по умолчанию могут использоваться только для подстановки вместо отсутствующих последних аргументов.

// Функция округляет val до digit цифр после

// запятой, по умолчанию - до 3го знака

double roundToDigit( double val, int digit = 3 )

{

  int factor = std::pow( 10.0, digit );

  int rounded = static_cast< int >( val * factor + 0.5 );

  return static_cast< double >( rounded ) / factor;

}

// Вызывающий код:

double x = roundToDigit( 1.2345 ); // x = 1.235

double y = roundToDigit( 1.2345, 1 ); // y = 1.2

Рекурсия.

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

// Функция вычисляет факториал n

unsigned int fact( unsigned int n )

{

  if ( n == 0 ) // Условие окончания рекурсии

     return 1;

  // Рекурсивно вызываем fact для меньшего n:

  return n * fact( n - 1 );

}

В этом примере функция вызывает сама себя n раз, до тех пор, пока параметр не уменьшится до 0. Последний вызов немедленно возвращает значение 1, и дальше это значение «всплывает» по стеку, пока управление не вернется назад в вызывающий код. Как правило, рекурсивные функции выполняются медленнее их итеративных аналогов, что связано с затратами на вызов функции. Тем не менее, для многих задач, рекурсивный подход представляется наиболее естественным и понятным способом реализации. Надо заметить, что многие оптимизирующие компиляторы способны неявно преобразовать некоторые виды рекурсии в итеративную схему вычислений.


 

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

83889. Особенности резекции желудка при язве. Реконструкция по Бильрот I. Реконструкция по Бильрот II. Техника выполнения 51.31 KB
  Этапы резекции желудка 1.Мобилизация скелетирование удаляемой части желудка пересечение сосудов желудка по малой и большой кривизне между лигатурами на протяжении участка резекции. Отсечение lig gstrocolicum от желудка следует начинать со средней трети большой кривизны.
83890. Особенности резекции желудка при раке. Реконструкция Бильрот II в модификации Гофмейстера – Финстерера. Техника выполнения, возможные осложнения и их профилактика 52.32 KB
  Этапы резекции желудка. Мобилизация скелетирование удаляемой части желудка пересечение сосудов желудка по малой и большой кривизне между лигатурами на протяжении участка резекции. После вскрытия брюшной полости производят тщательную ревизию ее определяя локализацию и степень поражения опухолью стенки желудка окружающих органов и тканей устанавливают степень поражения лимфатических узлов малого и большого сальника корня брыжейки забрюшинных лимфатических узлов и т.
83891. Гастрэктомия и резекция желудка по Ру. Показания, техника выполнения 48.63 KB
  Гастрэктомия хирургическое вмешательство подразумевающее тотальное полное удаление желудка с наложением пищеводнокишечного соустья анастомоза. Основным показанием к операции является рак желудка. На связки желудка накладываются зажимы питающие его сосуды лигируются.
83892. Принципы и техника наложения кишечного шва. Классификация кишечных швов 50.62 KB
  Сквозные швы являются инфицированными грязными. Швы не проходящие через слизистую оболочку называют неинфицированными чистыми. В зависимости от рядности кишечных швов однорядные швы Матешука нить проходит через края серозной мышечной оболочек и подслнзнстой основы без захвата слизистой оболочки что обеспечивает хорошую адаптацию краев и надежное погружение в просвет кишки слизистой оболочки без дополнительной ее травматизации: двухрядные швы Альберта используется в качестве первого ряда сквозной шов. поверх которого...
83893. Техника выполнения резекции тонкой кишки. Анастомоз по типу «конец в конец» 49.79 KB
  Техника выполнения резекции тонкой кишки Мобилизация резецируемого участка перевязка сосудов и пересечение брыжейки удаляемого сегмента. В зависимости от способа мобилизации выделяют прямую и клиновидную резекции тонкой кишки. Резекция кишки наложение эластических и раздавливающих кишечных зажимов по линии предполагаемого разреза в косом направлении для наложения энтероанастомоза конец в конец и рассечение органа между ними удаляя больше тканей на свободном противобрыжеечном крае кишки в настоящее время для уменьшения...
83894. Техника выполнения резекции тонкой кишки. Анастомоз по типу «бок в бок» 50.15 KB
  Техника выполнения резекции тонкой кишки. В зависимости от способа мобилизации выделяют прямую и клиновидную резекции тонкой кишки.Резекция кишки наложение эластических и раздавливающих кишечных зажимов по линии предполагаемого разреза в косом направлении для наложения энтероанастомоза конец в конец и рассечение органа между ними удаляя больше тканей на свободном противобрыжеечном крае кишки в настоящее время для уменьшения травматизации кишки зажимы не применяются а используются швыдержачки.
83895. Хирургическая анатомия тонкой кишки. Отделы, особенности кровоснабжения. Брыжеечные синусы 52 KB
  Отделы тонкой кишки: двенадцатиперстная кишка рассматривалась выше; тощая кишка; подвздошная кишка. Между листками брюшины по мезентериальному краю выделяют так называемое внебрюшинное поле re nud вдоль которого в стенку кишки вступают прямые артерии а из нее выходят прямые вены и экс траорганные лимфатические сосуды. Скелетотопия: корень брыжейки тонкой кишки начинается от L2 позвонка и опускается слева направо до крестцово подвздошного сустава пересекая горизонтальную часть двенадцатиперстной кишки аорту нижнюю полую вену...
83896. Хирургическая анатомия толстой кишки. Отделы, кровоснабжение, венозный отток. Боковые каналы 50.73 KB
  Отделы толстой кишки: Слепая кишка Восходящая ободочная кишка Правый изгиб ободочной кишки Поперечная ободочная кишка Левый изгиб ободочной кишки Нисходящая ободочная кишка Сигмовидная ободочная кишка Прямая кишка Кровоснабжение ободочной кишки осуществляется верхней и нижней брыжеечными артериями. Ветви верхней брыжеечной артерии: Подвздошноободочная артерия отдает ветви к терминальному отделу подвздошной кишки червеобразному отростку передние и задние слепокишечные артерии и восходящую артерию кровоснабжающую начальную...
83897. Хирургическая анатомия слепой кишки. Техника выполнения аппендэктомии при ретроперитонеальном расположении червеобразного отростка 50.91 KB
  Техника выполнения аппендэктомии при ретроперитонеальном расположении червеобразного отростка. Червеобразный отросток Варианты положения периферической части отростка нисходящее верхушка отростка обращена вниз и влево и достигает пограничной линии а иногда опускается в малый таз наиболее частый вариант; медиальное вдоль концевого отдела подвздошной кишки; латеральное в правом боковом канале; восходящее вдоль передней стенки слепой кишки; ретроцекальное и ретроперитонеальное в забрюшинной клетчатке. Проекция основания отростка...