Какое определение функции неверное для языка c. Функции в языке программирования C. Передача параметров функции main

25.11.2023

Функция - это группа операторов у которой есть имя. Во всех предыдущих уроках, код наших программ располагался в одной функции - main. Функции позволяют разбить программу на небольшие части, каждая из которых выполняет какую-то небольшую задачу. Посмотрите на полный код морского боя (раздел - Листинги). В нём больше 500 строк кода. Без функций было бы довольно проблематично написать эту программу. В ней используется 11 функций, самая длинная из которых состоит из 50 строк кода.

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

Определение функции

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

код на языке c++

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

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

int simple_function()
float simple_function()
char simple_function()

В первом случае функция должна вернуть целое число (int), во втором - вещественное число (float), а в третьем случае - символ (char).

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

Идентификатор (имя) функции задаётся точно также, как и любой другой идентификатор. В данном примере мы создали функцию с идентификатором simple_function (simple - простой).
Список аргументов или параметров

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

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

int simple (int a, float b)

В скобках мы записали два аргумента: a и b. У аргумента a тип int, а у аргумента b тип float.

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

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

код на языке c++ int simple_function () { return 0; }

Здесь, simple_function всегда будет возвращать 0. Надо признать, что данная функция бесполезна. Напишем функцию, которая принимает из вызывающего окружения два значения, складывает их и возвращает результат в вызывающее окружение. Назовём эту функцию sum (сумма):

код на языке c++ int sum (int a, int b) { int c; c = a + b; return c; }

В функцию передаётся два аргумента: a и b типа int. В теле функции они используются как обычные переменные (они и являются обычными переменными). Давайте договоримся: снаружи функции, переменные, которые передаются в неё, мы будем называть аргументами, а эти же переменные в теле функции - параметрами.

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

Последняя строчка возвращает значение переменной c во внешнее окружение.

После ключевого слова return нужно указать значение которое будет возвращено. Можно возвращать как простые значения, так и переменные и даже выражения. Например:

return 32;
return a;
return b;
return a+b;

В последнем случае в вызывающее окружение будет возвращён результат суммы переменных a и b.

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

return a;
c = a+b; // этот оператор не будет выполнен

Благодаря этому, с помощью return удобно создавать условия выхода из функций:

код на языке c++ if (a > 0) { return 0; } else if (a < 0) { return 1 }

Здесь, из функции будет возвращено число в зависимости от значения переменной a: если a больше нуля, то будет возвращён 0, в противном случае - 1.
Вызов функции

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

код на языке c++ int sum (int a, int b) { int c; c = a + b; return c; } int main() { int s; s = sum(2,2); // вызов функции cout << s; return 0; }

В результате выполнения программы, на экран будет выведено: 4.

Вызов функции состоит из идентификатора функции и списка аргументов в круглых скобках. Вот несколько вызовов функции sum:

int x = 5;
int y = 4;
int z;

sum(0,1); // 1
sum(x,2); // 7
sum(x,y); // 9
z = sum(x,y); // z = 9

Вызывающее окружение

То место, откуда вызывается функция, называется вызывающим окружением. Вызывающим окружением функции sum является функция main, а вызывающим окружением функции main является отладчик или операционная система.

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

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

код на языке c++ int simple (int a) { return 1; } int main () { int b; b = simple(0.5); return 0; }

В списке аргументов мы указали тип int, а в функцию передаётся вещественное значение 0.5. Так делать нельзя.

Рассмотрим более полезный пример: напишем функцию для передвижения персонажа:

код на языке c++ int x; // глобальная переменная int move_x (int dx) { x = x + dx; return x; } int main () { int ch; char act; while (1) { act = _getch(); ch = static_cast(act); if (ch == 75) // была нажата стрелочка влево move_x(-1); else if (ch == 77) // была нажата стрелочка вправо move_x(1); } // конец while return 0; } // конец main

Обратите внимание, что для тела if и else if не стоит скобочек. Скобочки ветвлений можно опускать, если в теле ветвления всего один оператор.

Функция move_x двигает персонажа на одну единицу влево или вправо в зависимости от клавиши, которую нажал пользователь.

Создадим ещё одну функцию, а заодно уберём из main часть кода:

код на языке c++ int x; // глобальная переменная int move_x (int dx) { x = x + dx; return x; } void move (int ch) { if (ch == 75) // была нажата стрелочка влево move_x(-1); else if (ch == 77) // была нажата стрелочка вправо move_x(1); } int main () { int ch; char act; while (1) { act = _getch(); ch = static_cast(act); move(ch); // вызов функции move } // конец while return 0; } // конец main

Обратите внимание, что в функции move на месте типа возвращаемого значения стоит void, кроме того, в теле move нет оператора return.

Объявления функций (прототипы)

В последнем примере, определение функции move_x должно располагаться выше определения move (а определения move_x и move должны располагаться до main), так как в функции move происходит вызов move_x.

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

код на языке c++ int x; // глобальная переменная void move(int); // прототип (объявление) функции move int move_x(int); // прототип функции move_x int main () { int ch; char act; while (1) { act = _getch(); ch = static_cast(act); move(ch); // вызов move } return 0; } void move (int ch) { if (ch == 75) // была нажата стрелочка влево move_x(-1); else if (ch == 77) // была нажата стрелочка вправо move_x(1); } int move_x (int dx) { x = x + dx; return x; }

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

int move_x(int dx);
int move_x(int);

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

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

Теги: Функции в си, прототип, описание, определение, вызов. Формальные параметры и фактические параметры. Аргументы функции, передача по значению, передача по указателю. Возврат значения.

Введение

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

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

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

Мы уже знакомы с многими функциями и знаем, как их вызывать – это функции библиотек stdio, stdlib, string, conio и пр. Более того, main – это тоже функция. Она отличается от остальных только тем, что является точкой входа при запуске приложения.
Функция в си определяется в глобальном контексте. Синтаксис функции: (, ...) { }

Самый простой пример – функция, которая принимает число типа float и возвращает квадрат этого числа

#include #include float sqr(float x) { float tmp = x*x; return tmp; } void main() { printf("%.3f", sqr(9.3f)); getch(); }

Внутри функции sqr мы создали локальную переменную, которой присвоили значение аргумента. В качестве аргумента функции передали число 9,3. Служебное слово return возвращает значение переменной tmp. Можно переписать функцию следующим образом:

Float sqr(float x) { return x*x; }

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

Void printSqr(float x) { printf("%d", x*x); return; }

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

Void printSqr(float x) { printf("%d", x*x); }

Если функция не принимает аргументов, то скобки оставляют пустыми. Можно также написать слово void:

Void printHelloWorld() { printf("Hello World"); }

эквивалентно

Void printHelloWorld(void) { printf("Hello World"); }

Формальные и фактические параметры

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

Например, пусть есть функция, которая возвращает квадрат числа и функция, которая суммирует два числа.

#include #include //Формальные параметры имеют имена a и b //по ним мы обращаемся к переданным аргументам внутри функции int sum(int a, int b) { return a+b; } float square(float x) { return x*x; } void main() { //Фактические параметры могут иметь любое имя, в том числе и не иметь имени int one = 1; float two = 2.0; //Передаём переменные, вторая переменная приводится к нужному типу printf("%d\n", sum(one, two)); //Передаём числовые константы printf("%d\n", sum(10, 20)); //Передаём числовые константы неверного типа, они автоматически приводится к нужному printf("%d\n", sum(10, 20.f)); //Переменная целого типа приводится к типу с плавающей точкой printf("%.3f\n", square(one)); //В качестве аргумента может выступать и вызов функции, которая возвращает нужное значение printf("%.3f\n", square(sum(2 + 4, 3))); getch(); }

Обращаю внимание, что приведение типов просиходит неявно и только тогда, когда это возможно. Если функция получает число в качестве аргумента, то нельзя ей передать переменную строку, например "20" и т.д. Вообще, лучше всегда использовать верный тип или явно приводить тип к нужному.
Если функция возвращает значение, то оно не обязательно должно быть сохранено. Например, мы пользуемся функцией getch, которая считывает символ и возвращает его.

#include #include void main() { char c; do { //Сохраняем возвращённое значение в переменную c = getch(); printf("%c", c); } while(c != "q"); //Возвращённое значение не сохраняется getch(); }

Передача аргументов

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

#include #include void change(int a) { a = 100; printf("%d\n", a); } void main() { int d = 200; printf("%d\n", d); change(d); printf("%d", d); getch(); }

Программы выведет
200
100
200
Понятно почему. Внутри функции мы работаем с переменной x, которая является копией переменной d. Мы изменяем локальную копию, но сама переменная d при этом не меняется. После выхода из функции локальная переменная будет уничтожена. Переменная d при этом никак не изменится.
Каким образом тогда можно изменить переменную? Для этого нужно передать адрес этой переменной. Перепишем функцию, чтобы она принимала указатель типа int

#include #include void change(int *a) { *a = 100; printf("%d\n", *a); } void main() { int d = 200; printf("%d\n", d); change(&d); printf("%d", d); getch(); }

Вот теперь программа выводит
200
100
100
Здесь также была создана локальная переменная, но так как передан был адрес, то мы изменили значение переменной d, используя её адрес в оперативной памяти.

В программировании первый способ передачи параметров называют передачей по значению, второй – передачей по указателю. Запомните простое правило: если вы хотите изменить переменную, необходимо передавать функции указатель на эту переменную. Следовательно, чтобы изменить указатель, необходимо передавать указатель на указатель и т.д. Например, напишем функцию, которая будет принимать размер массива типа int и создавать его. С первого взгляда, функция должна выглядеть как-то так:

#include #include #include void init(int *a, unsigned size) { a = (int*) malloc(size * sizeof(int)); } void main() { int *a = NULL; init(a, 100); if (a == NULL) { printf("ERROR"); } else { printf("OKAY..."); free(a); } getch(); }

Но эта функция выведет ERROR. Мы передали адрес переменной. Внутри функции init была создана локальная переменная a, которая хранит адрес массива. После выхода из функции эта локальная переменная была уничтожена. Кроме того, что мы не смогли добиться нужного результата, у нас обнаружилась утечка памяти: была выделена память на куче, но уже не существует переменной, которая бы хранила адрес этого участка.

Для изменения объекта необходимо передавать указатель на него, в данном случае – указатель на указатель.

#include #include #include void init(int **a, unsigned size) { *a = (int*) malloc(size * sizeof(int)); } void main() { int *a = NULL; init(&a, 100); if (a == NULL) { printf("ERROR"); } else { printf("OKAY..."); free(a); } getch(); }

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

#include #include #include #include char* initByString(const char *str) { char *p = (char*) malloc(strlen(str) + 1); strcpy(p, str); return p; } void main() { char *test = initByString("Hello World!"); printf("%s", test); free(test); getch(); }

В этом примере утечки памяти не происходит. Мы выделили память с помощью функции malloc, скопировали туда строку, а после этого вернули указатель. Локальные переменные были удалены, но переменная test хранит адрес участка памяти на куче, поэтому можно его удалить с помощью функции free.

Объявление функции и определение функции. Создание собственной библиотеки

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

#include #include //Прототипы функций. Имена аргументов можно не писать int odd(int); int even(int); void main() { printf("if %d odd? %d\n", 11, odd(11)); printf("if %d odd? %d\n", 10, odd(10)); getch(); } //Определение функций int even(int a) { if (a) { odd(--a); } else { return 1; } } int odd(int a) { if (a) { even(--a); } else { return 0; } }

Это смешанная рекурсия – функция odd возвращает 1, если число нечётное и 0, если чётное.

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

Давайте создадим простую библиотеку. Для этого нужно будет создать два файла – один с расширением.h и поместить туда прототипы функций, а другой с расширением.c и поместить туда определения этих функций. Если вы работаете с IDE, то.h файл необходимо создавать в папке Заголовочные файлы, а файлы кода в папке Файлы исходного кода. Пусть файлы называются File1.h и File1.c
Перепишем предыдущий код. Вот так будет выглядеть заголовочный файл File1.h

#ifndef _FILE1_H_ #define _FILE1_H_ int odd(int); int even(int); #endif

Содержимое файла исходного кода File1.c

#include "File1.h" int even(int a) { if (a) { odd(--a); } else { return 1; } } int odd(int a) { if (a) { even(--a); } else { return 0; } }

Наша функция main

#include #include #include "File1.h" void main() { printf("if %d odd? %d\n", 11, odd(11)); printf("if %d odd? %d\n", 10, odd(10)); getch(); }

Рассмотрим особенности каждого файла. Наш файл, который содержит функцию main, подключает необходимые ему библиотеки а также заголовочный файл File1.h. Теперь компилятору известны прототипы функций, то есть он знает возвращаемый тип, количество и тип аргументов и имена функций.

Заголовочный файл, как и оговаривалось ранее, содержит прототип функций. Также здесь могут быть подключены используемые библиотеки. Макрозащита #define _FILE1_H_ и т.д. используется для предотвращения повторного копирования кода библиотеки при компиляции. Эти строчки можно заменить одной

#pragma once int odd(int); int even(int);

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

Передача массива в качестве аргумента

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

#include #include void printArray(int *arr, unsigned size) { unsigned i; for (i = 0; i < size; i++) { printf("%d ", arr[i]); } } void main() { int x = {1, 2, 3, 4, 5}; printArray(x, 10); getch(); }

В этом примере функция может иметь следующий вид

Void printArray(int arr, unsigned size) { unsigned i; for (i = 0; i < size; i++) { printf("%d ", arr[i]); } }

Также напомню, что правило подмены массива на указатель не рекурсивное. Это значит, что необходимо указывать размерность двумерного массива при передаче

#include #include void printArray(int arr, unsigned size) { unsigned i, j; for (i = 0; i < size; i++) { for (j = 0; j < 5; j++) { printf("%d ", arr[i][j]); } printf("\n"); } } void main() { int x = { { 1, 2, 3, 4, 5}, { 6, 7, 8, 9, 10}}; printArray(x, 2); getch(); }

Либо, можно писать

#include #include void printArray(int (*arr), unsigned size) { unsigned i, j; for (i = 0; i < size; i++) { for (j = 0; j < 5; j++) { printf("%d ", arr[i][j]); } printf("\n"); } } void main() { int x = { { 1, 2, 3, 4, 5}, { 6, 7, 8, 9, 10}}; printArray(x, 2); getch(); }

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

#include #include #include #include #define SIZE 10 unsigned* getLengths(const char **words, unsigned size) { unsigned *lengths = NULL; unsigned i; lengths = (unsigned*) malloc(size * sizeof(unsigned)); for (i = 0; i < size; i++) { lengths[i] = strlen(words[i]); } return lengths; } void main() { char **words = NULL; char buffer; unsigned i; unsigned *len = NULL; words = (char**) malloc(SIZE * sizeof(char*)); for (i = 0; i < SIZE; i++) { printf("%d. ", i); scanf("%127s", buffer); words[i] = (char*) malloc(128); strcpy(words[i], buffer); } len = getLengths(words, SIZE); for (i = 0; i < SIZE; i++) { printf("%d ", len[i]); free(words[i]); } free(words); free(len); getch(); }

Можно вместо того, чтобы возвращать указатель на массив, передавать массив, который необходимо заполнить

#include #include #include #include #define SIZE 10 void getLengths(const char **words, unsigned size, unsigned *out) { unsigned i; for (i = 0; i < size; i++) { out[i] = strlen(words[i]); } } void main() { char **words = NULL; char buffer; unsigned i; unsigned *len = NULL; words = (char**) malloc(SIZE * sizeof(char*)); for (i = 0; i < SIZE; i++) { printf("%d. ", i); scanf("%127s", buffer); words[i] = (char*) malloc(128); strcpy(words[i], buffer); } len = (unsigned*) malloc(SIZE * sizeof(unsigned)); getLengths(words, SIZE, len); for (i = 0; i < SIZE; i++) { printf("%d ", len[i]); free(words[i]); } free(words); free(len); getch(); }

На этом первое знакомство с функциями заканчивается: тема очень большая и разбита на несколько статей.

Последнее обновление: 22.09.2017

Определение функции

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

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

Тип имя_функции(параметры) { инструкции }

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

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

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

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

Для возвращения результата функция применяет оператор return . Если функция имеет в качестве возвращаемого типа любой тип, кроме void, то она должна обязательно с помощью оператора return возвращать какое-либо значение.

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

Int main() { return 0; }

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

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

Void hello() { std::cout << "hello\n"; }

Выполнение функции

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

Имя_функции(аргументы);

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

Например, определим и выполним простейшую функцию:

#include void hello() { std::cout << "hello\n"; } int main() { hello(); hello(); return 0; }

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

Объявление функции

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

#include int main() { hello(); hello(); return 0; } void hello() { std::cout << "hello\n"; }

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

Тип имя_функции(параметры);

Фактически это заголовок функции. То есть для функции hello объявление будет выглядеть следующим образом:

Void hello();

Используем объявление функции:

#include void hello(); int main() { hello(); hello(); return 0; } void hello() { std::cout << "hello\n"; }

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

До сих пор мы писали программы единым, функционально неделимым, кодом. Алгоритм программы находился в главной функции, причём других функций в программе не было. Мы писали маленькие программы, поэтому не было потребности в объявлении своих функций. Для написания больших программ, опыт показывает, что лучше пользоваться функциями. Программа будет состоять из отдельных фрагментов кода, под отдельным фрагментом кода понимается функция. Отдельным, потому, что работа отдельной функции не зависит от работы какой-нибудь другой. То есть алгоритм в каждой функции функционально достаточен и не зависим от других алгоритмов программы. Однажды написав функцию, её можно будет с лёгкостью переносить в другие программы. Функция (в программировании) — это фрагмент кода или алгоритм, реализованный на каком-то языке программирования, с целью выполнения определённой последовательности операций. Итак, функции позволяют сделать программу модульной, то есть разделить программу на несколько маленьких подпрограмм (функций), которые в совокупности выполняют поставленную задачу. Еще один огромнейший плюс функций в том, что их можно многократно использовать. Данная возможность позволяет многократно использовать один раз написанный код, что в свою очередь, намного сокращает объем кода программы!

Кроме того, что в С++ предусмотрено объявление своих функций, также можно воспользоваться функциями определёнными в стандартных заголовочных файлах языка программирования С++. Чтобы воспользоваться функцией, определённой в заголовочном файле, нужно его подключить. Например, чтобы воспользоваться функцией, которая возводит некоторое число в степень, нужно подключить заголовочный файл ив запустить функцию pow() в теле программы. Разработаем программу, в которой запустим функцию pow() .

// inc_func.cpp: определяет точку входа для консольного приложения. #include "stdafx.h" //действие 1 - подключаем заголовочный файл

// код Code::Blocks

// код Dev-C++

// inc_func.cpp: определяет точку входа для консольного приложения. //действие 1 - подключаем заголовочный файл который содержит прототипы основных математических функций #include int main(int argc, char* argv) { float power = pow(3.14,2); //действие 2 - запуск функции возведения числа в степень return 0; }

Подключение заголовочных файлов выполняется так, как показано в строке 5 , т. е. объявляется препроцессорная директива #include , после чего внутри знаков <> пишется имя заголовочного файла. Когда подключен заголовочный файл, можно использовать функцию, что, и сделано в строке 9 .Функция pow() возводит число 3.14 в квадрат и присваивает полученный результат переменной power , где
pow — имя функции;
числа 3.14 и 2 — аргументы функции;

Всегда после имени функции ставятся круглые скобочки, внутри которых, функции передаются аргументы, и если аргументов несколько, то они отделяются друг от друга запятыми. Аргументы нужны для того, чтобы функции передать информацию. Например, чтобы возвести число 3.14 в квадрат используя функцию pow() , нужно как-то этой функции сообщить, какое число, и в какую степень его возводить. Вот именно для этого и придуманы аргументы функций, но бывают функции, в которых аргументы не передаются, такие функции вызываются с пустыми круглыми скобочками. Итак, для того, чтобы воспользоваться функцией из стандартного заголовочного файла С++ необходимо выполнить два действия:

  1. Подключить необходимый заголовочный файл;
  2. Запустить нужную функцию.

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

  1. Функции, которые не возвращают значений
  2. Функции, возвращающие значение

Функции, не возвращающие значения, завершив свою работу, никакого ответа программе не дают. Рассмотрим структуру объявления таких функций.

// структура объявления функций не возвращающих значений void /*имя функции*/(/*параметры функции*/) // заголовок функции { // тело функции }

Строка 2 начинается с зарезервированного слова void — это тип данных, который не может хранить какие-либо данные. Тип данных void говорит о том, что данная функция не возвращает никаких значений. void никак по-другому не используется и нужен только для того, чтобы компилятор мог определить тип функции. После зарезервированного слова void пишется имя функции. Сразу за именем функции ставятся две круглые скобочки, открывающаяся и закрывающаяся. Если нужно функции передавать какие-то данные, то внутри круглых скобочек объявляются параметры функции, они отделяются друг от друга запятыми. Строка 2 называется заголовком функции. После заголовка функции пишутся две фигурные скобочки, внутри которых находится алгоритм, называемый телом функции. Разработаем программу, в которой объявим функцию нахождения факториала, причём функция не должна возвращать значение.

<= numb; i++) // цикл вычисления значения n! rezult *= i; // накапливаем произведение в переменной rezult cout << numb << "! = " << rezult << endl; // печать значения n! } int main(int argc, char* argv) { int digit; // переменная для хранения значения n! cout << "Enter number: "; cin >> digit; faktorial(digit);// запуск функции нахождения факториала system("pause"); return 0; }

// код Code::Blocks

// код Dev-C++

using namespace std; // объявление функции нахождения n! void faktorial(int numb)// заголовок функции { int rezult = 1; // инициализируем переменную rezult значением 1 for (int i = 1; i <= numb; i++) // цикл вычисления значения n! rezult *= i; // накапливаем произведение в переменной rezult cout << numb << "! = " << rezult << endl; // печать значения n! } int main(int argc, char* argv) { int digit; // переменная для хранения значения n! cout << "Enter number: "; cin >> digit; faktorial(digit);// запуск функции нахождения факториала return 0; }

После того, как были подключены все необходимые заголовочные файлы, можно объявлять функцию нахождения факториала.Под объявлением функции подразумевается выбор имени функции, определение параметров функции и написание алгоритма, который является телом функции. После выполнения этих действий функцию можно использовать в программе. Так как функция не должна возвращать значение, то тип возвращаемых данных должен быть void . Имя функции — faktorial , внутри круглых скобочек объявлена переменная numb типа int . Эта переменная является параметром функции faktorial() . Таким образом, все объявления в строке 8 в совокупности составляют заголовок функции. Строки 9 — 14 составляют тело функции faktorial() . Внутри тела в строке 10 объявлена переменная rezult , которая будет хранить результат нахождения n! После чего, в строках 11-12 Объявлен оператор цикла for для нахождения факториала. В строке 13 объявлен оператор cout , с помощью которого значение факториала будет печататься на экране. Теперь, когда функция объявлена можно воспользоваться ею. В строке 21 запускается функция faktorial(digit) , внутри скобочек функции передаётся аргумент, т. е. значение, содержащееся в переменной digit . Результат работы программы (см. Рисунок 1).

Рисунок 1 — Функции в С++

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

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

// структура объявления функций возвращающих значения /*возвращаемый тип данных*/ /*имя функции*/(/*параметры функции*/) // заголовок функции { // тело функции return /*возвращаемое значение*/; }

Структура объявления функций осталась почти неизменной, за исключением двух строк. В заголовке функции сначала нужно определять возвращаемый тип данных, это может быть тип данных int , если необходимо возвратить целое число или тип данных float — для чисел с плавающей точкой. В общем, любой другой тип данных, всё зависит от того, что функция должна вернуть. Так как функция должна вернуть значение, то для этого должен быть предусмотрен специальный механизм, как в строке 5 . C помощью зарезервированного слова return можно вернуть значение, по завершении работы функции. Всё, что нужно, так это указать переменную, содержащую нужное значение, или некоторое значение, после оператора return . Тип данных возвращаемого значения в строке 5 должен совпадать с типом данных в строке 2 . Переделаем программу нахождения факториала так, чтобы функция faktorial() возвращала значение факториала.

// struct_func.cpp: определяет точку входа для консольного приложения. #include "stdafx.h" #include <= numb; i++) // цикл вычисления значения n! rezult *= i; // накапливаем произведение в переменной rezult return rezult; // передаём значение факториала в главную функцию } int main(int argc, char* argv) { int digit; // переменная для хранения значения n! cout << "Enter number: "; cin >> digit; cout << digit << "! = " << faktorial(digit) << endl;// запуск функции нахождения факториала system("pause"); return 0; }

// код Code::Blocks

// код Dev-C++

// struct_func.cpp: определяет точку входа для консольного приложения. #include using namespace std; // объявление функции нахождения n! int faktorial(int numb)// заголовок функции { int rezult = 1; // инициализируем переменную rezult значением 1 for (int i = 1; i <= numb; i++) // цикл вычисления значения n! rezult *= i; // накапливаем произведение в переменной rezult return rezult; // передаём значение факториала в главную функцию } int main(int argc, char* argv) { int digit; // переменная для хранения значения n! cout << "Enter number: "; cin >> digit; cout << digit << "! = " << faktorial(digit) << endl;// запуск функции нахождения факториала return 0; }

Теперь функция faktorial() имеет возвращаемый тип данных — int , так как n! — это целое число.В строке 13 объявлен оператор return , который возвращает значение, содержащееся в переменной rezult . В строке 21 выполняем запуск функции faktorial() ,возвращаемое значение которой отправляем в поток вывода с помощью оператора cout . Можно было бы написать так int fakt = faktorial(digit); — переменной типа int присваиваем возвращаемое значение функции faktorial() , после чего в переменной fakt будет храниться значение n! . Результат работы программы не изменился (см. Рисунок 2).

Enter number: 5 5! = 120 Для продолжения нажмите любую клавишу. . .

Рисунок 2 — Функции в С++

Мы рассмотрели два типа функций, причём объявление функций выполняли в области программы, после подключения заголовочных файлов, но до начала функции main() . Существует несколько способов объявления функций (см. Рисунок 3).

Рисунок 3 — Функции в С++

На рисунке 3 показаны 4 способа объявления функций в языке программирования С++. Рассмотрим структуры объявления функций в одном файле, с главной функцией. Функции можно объявлять в двух областях, до начала функции main() , после функции main() . До сих пор мы объявляли функции в одном файле, перед функцией main() — это самый простой из способов.

// struct_func.cpp: определяет точку входа для консольного приложения. #include "stdafx.h" /*область 1 - объявление функций до начала main() место для объявления функций функциям объявленным в этой области не нужны прототипы */ int main(int argc, char* argv) { return 0; }

Если функции объявлять в области 1 , перед главной функцией, то прототипы для этих функций не нужны. Хорошему стилю программирования соответствует способ объявления функций после main() . Рассмотрим структуру такого объявления функций.

// struct_func.cpp: определяет точку входа для консольного приложения. #include "stdafx.h" // место для объявления прототипов функций int main(int argc, char* argv) { return 0; } /*область 2 - объявление функций после main() место для объявления функций */

// код Code::Blocks

// код Dev-C++

// struct_func.cpp: определяет точку входа для консольного приложения. // место для объявления прототипов функций int main(int argc, char* argv) { return 0; } /*область 2 - объявление функций после main() место для объявления функций */

Область объявления функций переместилась в самый конец программы, после main() . Что касается самого способа объявления функций, то он не поменялся. Но так как функции объявлены после main() , использовать их не получится, ведь порядок объявлений изменился и функция main() просто не будет видеть функции объявленные после. Так вот для того, чтобы эти функции можно было увидеть в main() существует понятие прототипа. Прототип функции — это заголовок функции, который объявляется перед функцией main() . И если объявить прототип функции, тогда функцию можно будет увидеть в main() .

// синтаксис объявления прототипа /*тип возвращаемых данных функции*/ /*имя функции*/(/*параметры функции*/);

Структура объявления прототипа очень схожа со структурой объявления функции. Разработаем программу, которая определяет, является ли введённое пятизначное число палиндромом. Палиндром — это число или текст, который читается одинаково как слева, так и справа: 93939; 49094; 11311.

// palindrom_func.cpp: определяет точку входа для консольного приложения. #include "stdafx.h" #include << "Enter 5zn-e chislo: "; // введите пятизначное число int in_number, out_number; // переменные для хранения введённого пятизначного числа cin >> << "Number " << out_number << " - palendrom" << endl; else cout<<"This is not palendrom"<

// код Code::Blocks

// код Dev-C++

// palindrom_func.cpp: определяет точку входа для консольного приложения. #include using namespace std; bool palindrom5(int); // прототип функции нахождения палиндрома пятизначных чисел int main(int argc, char* argv) { cout << "Enter 5zn-e chislo: "; // введите пятизначное число int in_number, out_number; // переменные для хранения введённого пятизначного числа cin >> in_number; out_number = in_number; // в переменную out_number сохраняем введённое число if (palindrom5(in_number)) // если функция вернёт true, то условие истинно, иначе функция вернёт false - ложно cout << "Number " << out_number << " - palendrom" << endl; else cout<<"This is not palendrom"<

В строке 7 объявлен прототип функции нахождения палиндрома пятизначных чисел. Обратите внимание на то, что прототип полностью должен совпадать с заголовком функции, но некоторые отличия все же есть. Например, то, что в прототипе имена параметров перечислять не надо, достаточно объявить их типы данных. В конце объявления прототипа всегда нужно ставить точку с запятой. В остальном объявление прототипа совпадает с объявлением заголовка одной функции. Прототип необходимо объявлять для каждой функции отдельно. Переменная out_number служит для временного хранения введённого числа. В строке 16 в условии оператора выбора if выполняется запуск функции palindrom5() . Аргументом для неё является переменная in_number . функция вернёт значение типа bool , и если функция вернёт true , то условие будет истинно, в противном случае — ложно. Можно было бы сначала присвоить значение, возвращаемое функцией, некоторой переменной, а потом эту переменную подставить в условие оператора выбора if , но это бы увеличило код программы на одну строку. В строках 23 — 40 объявлена функция palindrom5() , с одним параметром, через который функции передаётся пятизначное число. Переменные balance1 , balance2 , balance4 , balance5 объявлены в строке 25 , и необходимы для хранения разрядов пятизначного числа: первого, второго, четвёртого, пятого (нумерация — справа на лево). В строках 26, 29, 32, 35 выполняется одна и та же операция — остаток от деления. Операция остаток от деления отсекает по одному разряду справа налево и сохраняет их в переменные balance1 , balance2 , balance4 , balance5 соответственно. Причём операция остаток от деления чередуется с операцией обычного деления. Операция деления выполняется в строках 27 , 30 , 33 , и уменьшает введённое пятизначное число за шаг на один разряд. В строке 30 операция деления уменьшает введённое число сразу на два разряда, потому, что число пятизначное и средний разряд нас не интересует, он может быть любым. В строках 36 — 39 объявлен оператор выбора if , который сравнивает разряды пятизначного числа, и если они, соответствующим образом, равны, то функция вернёт значение true , иначе — false . Результат работы программы (см. Рисунок 4).

Enter 5zn-e chislo: 12321 Number 12321 - palendrom Для продолжения нажмите любую клавишу. . .

Рисунок 4 — Функции в С++

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

  1. создание файла типа *.cpp, в котором объявляются функции;
  2. создание файлов типа *.cpp и *.h.

К хорошему стилю программирования относится второй способ. Таким образом, если объявлять функции в другом файле, то делать это согласно способу два. Переделаем программу нахождения палиндрома так, чтобы объявление функции palindrom5() находилось в отдельном файле *.cpp . Файл *.h нужен для того, чтобы скрыть реализацию функций, т. е. в файле *.h будут содержаться прототипы функций. С помощью обозревателя решений MVS создаём файл типа *.h и называем его palendrom .

// код файла palendrom.h #ifndef palendrom #define palendrom bool palindrom5(int); // прототип функции нахождения палиндрома пятизначных чисел #endif

Директивы в строках 2,3,5 необходимо всегда объявлять в файлах с прототипами функций, причём прототипы функций всегда объявляются в файлах типа *.h . После директив записанных в строках 2,3 , но до директивы #endif объявляются прототипы функций. В строке 4 объявлен прототип функции palindrom5() . Объявление данной функции находится в файле palendrom.cpp , который предварительно тоже был создан с помощью обозревателя решений MVS.

// содержимое файла palendrom.cpp #include "stdafx.h" #include "palendrom.h" bool palindrom5(int number) // функция нахождения палиндрома пятизначных чисел { int balance1, balance2, balance4, balance5; // переменные хранящие промежуточные результаты balance1 = number % 10; // переменной balance1 присвоили первый остаток number = number / 10; // уменьшаем введённое число на один разряд balance2 = number % 10; // переменной balance2 присвоили второй остаток number = number / 100; // уменьшаем введённое число на два разряда balance4 = number % 10; // переменной balance4 присвоили четвёртый остаток number = number / 10; // уменьшаем введённое число на один разряд balance5 = number % 10; // переменной balance5 присвоили пятый остаток if ((balance1 == balance5) && (balance2 == balance4)) return true; // функция возвращает истинное значение else return false; // функция возвращает ложное значение }

// код Code::Blocks

// код Dev-C++

// содержимое файла palendrom.cpp #include "palendrom.h" bool palindrom5(int number) // функция нахождения палиндрома пятизначных чисел { int balance1, balance2, balance4, balance5; // переменные хранящие промежуточные результаты balance1 = number % 10; // переменной balance1 присвоили первый остаток number = number / 10; // уменьшаем введённое число на один разряд balance2 = number % 10; // переменной balance2 присвоили второй остаток number = number / 100; // уменьшаем введённое число на два разряда balance4 = number % 10; // переменной balance4 присвоили четвёртый остаток number = number / 10; // уменьшаем введённое число на один разряд balance5 = number % 10; // переменной balance5 присвоили пятый остаток if ((balance1 == balance5) && (balance2 == balance4)) return true; // функция возвращает истинное значение else return false; // функция возвращает ложное значение }

В файле palendrom.cpp находится объявление функции palindrom5() . Так как файл palendrom.cpp является исполняемым файлом (*.cpp — исполняемые файлы), то в нём обязательно нужно подключить контейнер "stdafx.h" , как в строке 2 . Чтобы связать файл, где объявлена функция palindrom5() и файл с её прототипом, подключим заголовочный файл (файл с прототипом), это сделано в строке 3 . Обратите внимание на то, что при подключении созданного нами файла используются двойные кавычки, а не знаки больше, меньше. Осталось только запустить функцию palindrom5() в главном исполняемом файле func_palendrom.cpp .

// func_palendrom.cpp: определяет точку входа для консольного приложения. #include "stdafx.h" #include << "Enter 5zn-e chislo: "; // введите пятизначное число int in_number, out_number; // переменные для хранения введённого пятизначного числа cin >> in_number; out_number = in_number; // в переменную out_number сохраняем введённое число if (palindrom5(in_number)) // если функция вернёт true, то условие истинно, иначе функция вернёт false - ложно cout << "Number " << out_number << " - palendrom" << endl; else cout<<"This is not palendrom"<

// код Code::Blocks

// код Dev-C++

// func_palendrom.cpp: определяет точку входа для консольного приложения. #include // подключение заголовочного файла, с прототипом функции palindrom5() #include "palendrom.h" using namespace std; int main(int argc, char* argv) { cout << "Enter 5zn-e chislo: "; // введите пятизначное число int in_number, out_number; // переменные для хранения введённого пятизначного числа cin >> in_number; out_number = in_number; // в переменную out_number сохраняем введённое число if (palindrom5(in_number)) // если функция вернёт true, то условие истинно, иначе функция вернёт false - ложно cout << "Number " << out_number << " - palendrom" << endl; else cout<<"This is not palendrom"<

В строке 6 мы подключили файл с прототипом функции palindrom5() , после чего можно использовать эту функцию. Итак, мы разбили программу на три файла:

  • файл проекта: func_palendrom.cpp
  • заголовочный файл palendrom.h
  • исполняемый файл palendrom.cpp

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