Определение, описание и вызов функции


При программировании на языке Си++ функция — это основное понятие, без которого невозможно обойтись. Во-первых, каждая программа обязательно должна включать единственную функцию с именем main (главная функция). Именно функция main обеспечивает создание точки входа в откомпилированную программу. Кроме функции с именем main, в программу может входить произвольное количество неглавных функций, выполнение которых инициализируется прямо или опосредовано вызовами из функции main. Всем именам функций программы по умолчанию присваивается класс памяти extern, т.е. каждая функция имеет внешний тип компоновки и статическую продолжительность существования. Как объект с классом памяти extern, каждая функция глобальна, т.е. при определенных условиях доступна во всех модулях программы. Для того, чтобы функцией можно было воспользоваться в модуле (вызвать функцию) она должна быть определена или описана до её вызова.

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

тип_функции имя_функции(список_формальных_параметров) { тело функции}

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

тип имя_параметра

или

тип имя_параметра = умалчиваемое значение,

т.е. для параметра может быть задано, а может отсутствовать умалчиваемое значение.

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

return выражение

или

return;

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

Приведём примеры определения функций с разными сигнатурами.

void printstr(char* name = “Компилятор версии ”, int ver = 311){

cout<<name<<ver;

}

Printstr();

Printstr(“Программа версии”);

Printstr(“Программа версии”, 45);

Printstr( , 45); //ошибка

 

 

Функция printstr не возвращает никакого значения (тип_функции void) и имеет два формальных параметра типа char* и int. Причём первому и второму параметру присвоено значение по умолчанию.

int cub( double x){ return x*x*x;}

Функция возвращает куб числа. Поскольку тип функции — int, а тип значения выражения в операторе return — double, то будет происходить неявное преобразование типа из double в int c потерей точности. Поэтому можно предположить, что скорее всего допущена ошибка и int нужно заменить на double.

double max(double x, double y){

if(x>y) return x;

return y;

}

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

double Norma(double x1, double y1, double x2, double y2, double)

{ return x2-x1>y2-y1?x2-x1:y2-y1;}

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

struct Point3D {

int x, y, z;

};

Point3D foo(Point3D a)

{

return{4, 5, 6 }; // возвращаем "кортеж"

}

void main()

{

Point3D pt { 1, 2, 3 };

pt = foo({ 3,2,1 }); // передаем "кортеж"

}

 

 

Описание (прототип) функции.Правила языка Си++ требуют, чтобы до вызова функции в теле модуля присутствовало определение или описание функции. Это необходимо для проверки компилятором соответствия количества и типов формальных и фактических параметров. Поскольку функция может быть определена только один раз, а вызываться много раз и в разных модулях, то без использования описания очень часто невозможно обойтись. Описание (прототип) внешне фактически полностью совпадает с заголовком функции и имеет вид

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

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

Приведем примеры описания функций, определенных выше.

void printstr(char*, int );

void printstr(char* name , int ver );

int cub( double x);

int cub( double );

double max(double , double );

double max(double x, double y);

double Norma(double , double , double , double , double);

double Norma(double x1, double y1, double x2, double y2, double z);

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

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

имя_функции(список фактических параметров);

Значением выражения "вызов функции" является возвращаемое функцией значение, тип которого соответствует типу функции.

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

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

//Программа 6.1

#include "stdafx.h"

#include <iostream>

int max(int n, int m){ return n<m?m:n;} // Определение до вызова функции

void main(void){ // Главная функция

void print(char *, int); // Прототип до определения

float cube(float x = 0) ; // Прототип до определения

int sum =5, k = 2;

// Вложенные вызовы функций:

sum = max((int)cube(float(k)), sum);

print("\nsum = ",sum);

getchar();

}

void print(char * name, int value){ // Определение функции

std::cout << "\n" << name << value;

}

float cube(float x){ // Определение функции

return x * x * x;

}

Результат выполнения программы:

sum = 8

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

Для функции max() прототип не потребовался ее определение размещено в том же файле до вызова функции. Прототипы функций print() и cube() в программе необходимы, так как определения функций размещены после обращения к ним.

Если программа состоит из нескольких файлов, то описание своих функций программист обычно вставляет в свой заголовочный файл мой_файла.h, который подключается к модулям программы с помощью директивы #include “ мой_файла.h”. Двойные кавычки как раз указывают на то, что файл мой_файла.h – это файл, созданный программистом, а не библиотечный файл.

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

void print(char* name = "Номер дома: ", int value = 1){

cout « "\n" « name « value;

}

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

print (); // Выводит: 'Номер дома: 1'

print("Номер комнаты: "); // Выводит: 'Номер комната: 1

print (,15); /* Ошибка - можно опускать только параметры, начиная с конца их списка*/

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

void display (int value = 1, char *name = "Номер дома:") {

cout « "\n" « name « value;

}

Обращения к ней могут быть такими:

display(); // Выводит: 'Номер дома: 1

display(15); // Выводит: 'Номер дома: 15'

display(6,"Размерность пространства:"); /* Выводит: Размерность пространства: 6*/

Стандарт 11 языка С++ позволяет использовать ещёодну форму определения и описания функций, которую называют «trailing синтаксисом». Например, функцию вида: int func_name(int x, int y); можно записать следующим образом:

auto func_name(int x, int y) -> int;

 



Дата добавления: 2020-12-11; просмотров: 376;


Поиск по сайту:

Воспользовавшись поиском можно найти нужную информацию на сайте.

Поделитесь с друзьями:

Считаете данную информацию полезной, тогда расскажите друзьям в соц. сетях.
Poznayka.org - Познайка.Орг - 2016-2024 год. Материал предоставляется для ознакомительных и учебных целей.
Генерация страницы за: 0.017 сек.