Урок №12. Функции

  Юрий  | 

  |

  Обновл. 19 Мар 2024  | 

 303698

 ǀ   58 

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

Функции

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

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

Функция, в которой находится вызов, называется caller, а функция, которую вызывают — вызываемая функция, например:

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

Starting main()
In doPrint()
Ending main()

Эта программа начинает выполнение с первой строки функции main(), где выводится на экран следующая строка: Starting main(). Вторая строка функции main() вызывает функцию doPrint(). На этом этапе выполнение стейтментов в функции main() приостанавливается и процессор переходит к выполнению стейтментов внутри функции doPrint(). Первая (и единственная) строка в doPrint() выводит текст In doPrint(). Когда процессор завершает выполнение doPrint(), он возвращается обратно в main() к той точке, на которой остановился. Следовательно, следующим стейтментом является вывод строки Ending main().

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

Правило: Не забывайте указывать круглые скобки () при вызове функций.

Возвращаемые значения


Когда функция main() завершает свое выполнение, она возвращает целочисленное значение обратно в операционную систему, используя оператор return.

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

Затем, внутри вызываемой функции, мы используем оператор return, чтобы указать возвращаемое значение — какое именно значение будет возвращаться обратно в caller.

Рассмотрим простую функцию, которая возвращает целочисленное значение:

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

7
10

Разберемся детально:

   Первый вызов функции return7() возвращает 7 обратно в caller, которое затем передается в std::cout для вывода.

   Второй вызов функции return7() опять возвращает 7 обратно в caller. Выражение 7 + 3 имеет результат 10, который затем выводится на экран.

   Третий вызов функции return7() опять возвращает 7 обратно в caller. Однако функция main() ничего с ним не делает, поэтому ничего и не происходит (возвращаемое значение игнорируется).

Примечание: Возвращаемые значения не выводятся на экран, если их не передать объекту std::cout. В последнем вызове функции return7() значение не отправляется в std::cout, поэтому ничего и не происходит.

Тип возврата void

Функции могут и не возвращать значения. Чтобы сообщить компилятору, что функция не возвращает значение, нужно использовать тип возврата void. Взглянем еще раз на функцию doPrint() из вышеприведенного примера:

Эта функция имеет тип возврата void, который означает, что функция не возвращает значения. Поскольку значение не возвращается, то и оператор return не требуется.

Вот еще один пример использования функции типа void:

В первом вызове функции returnNothing() выводится Hi!, но ничего не возвращается обратно в caller. Точка выполнения возвращается обратно в функцию main(), где программа продолжает свое выполнение.

Второй вызов функции returnNothing() даже не скомпилируется. Функция returnNothing() имеет тип возврата void, который означает, что эта функция не возвращает значения. Однако функция main() пытается отправить это значение (которое не возвращается) в std::cout для вывода. std::cout не может обработать этот случай, так как значения на вывод не предоставлено. Следовательно, компилятор выдаст ошибку. Вам нужно будет закомментировать эту строку, чтобы компиляция прошла успешно.

Возврат значений функцией main()


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

Почему нужно возвращать значения обратно в операционную систему? Дело в том, что возвращаемое значение функции main() является кодом состояния, который сообщает операционной системе об успешном или неудачном выполнении программы. Обычно, возвращаемое значение 0 (ноль) означает что всё прошло успешно, тогда как любое другое значение означает неудачу/ошибку.

Обратите внимание, по стандартам языка C++ функция main() должна возвращать целочисленное значение. Однако, если вы не укажете return в конце функции main(), компилятор возвратит 0 автоматически, если никаких ошибок не будет. Но рекомендуется указывать return в конце функции main() и использовать тип возврата int для функции main().

Еще о возвращаемых значениях

Во-первых, если тип возврата функции не void, то она должна возвращать значение указанного типа (использовать оператор return). Единственно исключение — функция main(), которая возвращает 0, если не предоставлено другое значение.

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

Функция может возвращать только одно значение через return обратно в caller. Это может быть либо число (например, 7), либо значение переменной, либо выражение (у которого есть результат), либо определенное значение из набора возможных значений.

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

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

Повторное использование функций


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

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

Enter an integer: 4
Enter an integer: 9
4 + 9 = 13

Здесь main() прерывается 2 раза. Обратите внимание, в обоих случаях, полученное пользовательское значение сохраняется в переменной x, а затем передается обратно в main() с помощью return, где присваивается переменной a или b!

Также main() не является единственной функцией, которая может вызывать другие функции. Любая функция может вызывать любую другую функцию!

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

Starting main()
O
K
Ending main()

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

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

Правильно вот так:

Тест

Какие из следующих программ не скомпилируются (и почему), а какие скомпилируются (и какой у них результат)?

Программа №1:

Программа №2:

Программа №3:

Программа №4:

Программа №5:

Программа №6:

Программа №7:

Ответы

Чтобы просмотреть ответ, кликните на него мышкой.

Ответ №1

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

Ответ №2

Эта программа не скомпилируется. Вложенные функции запрещены.

Ответ №3

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

Ответ №4

Эта программа не скомпилируется, так как тип возврата функции printO() — void, а мы отправляем несуществующее возвращаемое значение на вывод. Результат — ошибка компиляции.

Ответ №5

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

6
6

Оба раза, когда вызывается функция getNumbers(), возвращается значение 6. Компилятор, встречая первый return, сразу же выполняет возврат этого значения, и всё, что находится за первым оператором return, — игнорируется. Строка return 8; никогда не выполнится.

Ответ №6

Эта программа не скомпилируется из-за недопустимого имени функции.

Ответ №7

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

Оценить статью:

Звёзд: 1Звёзд: 2Звёзд: 3Звёзд: 4Звёзд: 5 (1 329 оценок, среднее: 4,88 из 5)
Загрузка...

Комментариев: 58

  1. Сергей:

    Фактически, если функция в С++ с типом (void) не возвращает никакого значения и не оказывает никакого побочного эффекта, то это значит, что данная функция никак не влияет на работу программы?

  2. Артем:

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

  3. арт:

    Здравствуйте. Отличный сайт, пришёл со всяких курсов, так как там мало рассказывают.
    Такой вопрос: зачем в конце main() необходимо писать "return 0"? Это значение ведь никуда не отправляется. А программа выполняется и без return вообще. Просто я механик, если винтик не выполняет какую-либо задачу, то зачем он в этом месте?)
    Второй вопрос: почему void так активно позиционирует себя как функция без возврата значения? И наоборот: любая другая функция постоянно требует возвращать значение, несмотря на то, что этого не нужно. Например функция всего-лишь выводит "О":

    Зачем ей return, которым я не воспользуюсь?
    Может я сейчас спрашиваю что-то вроде "почему танк стреляет, а не поля сеет", но мне хочется понять)

    1. Фото аватара Юрий:

      Здравствуйте!

      Ответ на первый вопрос находится в данном уроке:

      «Почему нужно возвращать значения обратно в операционную систему? Дело в том, что возвращаемое значение функции main() является кодом состояния, который сообщает операционной системе об успешном или неудачном выполнении программы. Обычно, возвращаемое значение 0 (ноль) означает что всё прошло успешно, тогда как любое другое значение означает неудачу/ошибку.»

      Второй вопрос:

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

      «Любая другая функция постоянно требует возвращать значение, несмотря на то, что этого не нужно» — в программировании есть определенные правила, нарушая которые вы получаете последствия: сбои (высокая критичность), ошибки (высокая критичность), неточности (средняя критичность), предупреждения (низкая критичность). В указанном вами примере вы получите предупреждение (редко, ошибку), что так делать нельзя. Если вам не нужно возвращать значение, а всего лишь что-то вывести — пожалуйста, для подобной задачи придуман тип void. Если вам нужно возвращать значение, значит тип void использовать нельзя.

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

    2. Тим:

      На практике мне удалось раз запечатлеть этот момент. Если из под винды запускать .ехе в консоли, то можно отловить как раз таки этот возвращаемый код состояния. Например, при вызове линкера (например clang) можно по возвращаемому коду узнать собралась ли программа или нет (использовал это в Visual Studio Code, чтобы после сборки он мне сразу запускал программу).

  4. Constantin:

    Я учу чистый Си с нуля. Но ваш урок помог мне найти ответ на мой вопрос как вернутся из нескольких функций обратно в главную функцию main();

    Спасибо. Очень хорошие у вас уроки по С++. Друг программист сказал сначало надо понять основы поэтому учу чистый С по книге Брайан Керниган, Деннис Ритчи. Вроде идёт, мне нравится) Я ещё читаю информацию на стороних сайтах

  5. Юрий:

    Прекрасные уроки. Рекомендую ВСЕМ! Молодцы.

  6. Ton:

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

  7. Jaroshevskii:

    Спасибо большое.

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

    Ещё хотелось бы уточнить по поводу using namespace std;. Стоит ли его использовать? Я понимаю что сейчас когда программы совсем маленькие это не имеет значения, но всё же в перспективе на будущее, чтобы сразу привыкнуть к std::?

    1. Фото аватара Юрий:

      Пожалуйста)

      Об std:: и using namespace std есть отдельный урок (дальше), на котором все важные моменты рассматриваются.

      1. Дорброжелатель:

        Несколько лет назад бросил учить C++ потому что не было вот таких понятных уроков.
        Спасибо тебе Юра, я надеюсь, что наконец-то одолею себя и пойду в геймдев,а не работу за кассой)

  8. AHTOH:

    Отличные уроки!
    Большое спасибо автору.
    И как замечательно, что после каждого урока есть комментарии пользователей. Мне кажется даже что без них уроки были бы не полными.
    Так что читать и вникать в комментарии рекомендую всем!

  9. GreenMetalSoul:

    Я удивлён что вы продолжаете оказывать поддержку на протяжении 3-х лет (как я смотрю по коментариям), и судя по всему, материал вы так же изменяете, я прав?

    1. Фото аватара Юрий:

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

      По поводу комментариев — публикую почти все комментарии (вменяемые), отвечаю сейчас не на все.

  10. Алина:

    Здраствуйте! У меня не считается программа 5. Проверила, код написан правильно. Ошибку показывает когда вписываю

    Ошибка именно с return 8;. Почему, не понимаю.

    1. Фото аватара Юрий:

      Функция не может иметь больше одного оператора return. Также return может возвращать только 1 значение.

  11. Александр:

    Здравствуйте, можно ли через return возвращать в функцию main переменные типа double?

  12. Ka3:

    Есть вопрос, может не совсем по теме, но всё же. Как мне вернуть while на самого себя, если все условия не соблюдены при помощи return…

    1. Иван:

      Хоть и поздно, но вдруг ещё актуально. Return возвращает значение из функции, а не из цикла. В вашем случае цикл и так будет бесконечным при несоблюдении условий, также можно возвращаться на начало через continue. Если вам нужен счётчик некорректных вводов, в else можно объявить static переменную и каждый раз её инкрементировать, а потом вывести значение.

  13. Менфис:

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

    1. Фото аватара Юрий:

      Пожалуйста 🙂

  14. Денис:

    Информация заходит легко и понятно. Респект автору!

  15. Rejep:

    thank you !
    Спасибо братан большое это очень круто:)

  16. Fray:

    Юрий, большое спасибо за ваши труды и разложенный по полочкам материал! Удачи во всем!

    1. Фото аватара Юрий:

      Пожалуйста 🙂

  17. APTEM:

    Привет Автору статьи, спасибо что так всё расписал.

    1. Фото аватара Юрий:

      Привет, пожалуйста)))

  18. Александр:

    При много благодарности!

  19. Артем:

    Здравствуйте! Написал у себя код по вашему примеру, но почему-то у меня visual studio ругается на не объявленные идентификаторы cout и endl

    1. Михаил:

      Попробуй напиши не cout, a std::cout( std::endl)

  20. Александр:

    В языке C++ понятия "процедура" нет? Я раньше немного знакомился с делфи, так там, на сколько я помню, функция должна возвращать какое либо значение, о процедуре нет.

    1. Анастасия:

      Процедура в С++ — это как функция с типом возвращаемого значения void

  21. Александр:

    Важный момент при написании текста программы, я возможно где то упустил, но функция main должна быть записана всегда внизу? А вызываемые функции вверху? И более поздняя вызываемая функция выше предыдущей? Или это не пренципиально?

    1. Анастасия:

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

    2. Massimo Modi:

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

  22. Сергей:

    Большое спасибо за сайт!

  23. Денис:

    А для чего это вообще нужно? Тот же Void, который не возвращает. Можно же просто не прописывать столько строк, а написать то, что нужно, присвоив то или иное к чему-то и выполнив действие.

    1. Константин:

      Я использовал void() для обработки большого числа данных по одному какому-то предмету и в ходе выполнения получал ответ за ответом. А другой void() у меня работает с другим предметом.

  24. Захар:

    // здраствуйте я правильно понял вопрос и выполнил задание ?

  25. Константин:

    Юра, в примере с printOK(), main(), выполняя код строку за строкой, видит printOK(), вызывает его и не дожидаясь ответа (т.к. он же void), продолжает свою работу, а тот в свою очередь также поступает с printO() и с printK() и все они параллельно работают или прерывают свое выполнение (а если так, то как они понимают где должна оказаться точка выполнения) и как возобновляют выполнение — ведь return-а у void-а нет
    Поясни, пожалуйста.

    1. Данила:

      вот как я понял там функциz возврата не нужна,так как функция вызывает другую функцию ,и так каждая функция по очереди . Это как раз пример как любая функция может дать вызов . Майн вызывает ПринтОК , функция ПринтОК ,в свою очередь (по запросу майн) вызывает уже две функции ПринтО и ПринтК. А Майн спокойно ждет ,когда эти две функции придут к ней по её вызовам.:D

  26. Ray:

    Доброго времени суток!

    Сайт наилучший, все очень доходчиво. Большое спасибо!

    Собрал примеры с нескольких уроков в одном примере и возникла пара вопросов:

    Компиляция выдает следующее:

    Starting main()
    Enter a number: 7
    Your number is: 7
    Inter an integer: 1
    Inter an integer: 2
    Inter an integer: 3
    Inter an integer: 4
    Inter an integer: 5
    2+3+4-5
    Ending main()
    Program ended with exit code: 0

    Вопрос: почему при компиляции, при многократном применении функции, возвращенных значений получается 5 (хотя переменных было 4:a,b,c,d ) ? и почему они начинаются со второго значения,т.е.2+3+4-5, а не 1+2+3-4 ?
    Заранее благодарен.

    1. Фото аватара Юрий:

      Так посмотрите сами у себя в функции main() код: что и за чем выполняется.

      Вы сначала вызываете doPrint — первое значение получаете (7). Затем вызываете getValueFromUsers — получаете второе значение (1). Затем инициализируете 4 переменные: a(2), b(3), c(4), d(5). Затем выполняете операции с 4 значениями: a + b + c — d. Второе значение getValueFromUsers (1) вы не присвоили никакой переменной в функции main(), только вывели в консоль, потому и значений 5. Почему вы добавляете 4 переменные и первое число 2, а не 1 — потому что отсчет начинается с переменной a(2).

    2. Максим:

      Почему я такой не доходчивый?! Я понимаю 50 на 50, но написать ничего не могу(

    3. Данила:

      у вас ,после допринт идет лишняя строка ,она обозначается как 1 ,её надо убрать и тогда 1 = а.

      1. Fray:

        Мне кажется автор комментария подразумевал, что для возврата какого-нибудь значения из функции GetValueFromUsers () нужно иметь место переменную, куда можно вывести это значение. Но даже если она и есть, остается вопрос, почему после первого вызова GetValueFromUsers () ее возвратное значение выводится на экран консоли, хотя команды std::cout не было?
        Извиняюсь, если я что то неправильно пишу, я еще ламер

        1. Александр:

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

  27. Михаил:

    Привет)). Скажи пожалуйста я правильно понял, что если функция к примеру эта:

    инструкция return должна быть обязательно тоже х? Я просто к тому,что у тебя в примерах я заметил,что ты в return указываешь число,то в caller оно и возвращается.

    1. Фото аватара Юрий:

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

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

  28. илья:

    странно, мне 10 лет но я что то понимаю

    1. Фото аватара Юрий:

      Ничего странного, если есть желание — будут возможности.

  29. beksheikh:

    Здравствуйте.скажите в институте учился и мне не говорили о ретурне.вместо этого использую getch(); подскажите в чем разница? Сначала добавляю библиотеку <conio.h> и потом в конце пишу гетч.и Второй вопрос.Как вы пишите std::cout(endl,cin) вместо этого научили писать в начале using namespace std; и потом вместо std::cout и тд пишу без стд.скажите как правильнее?)заранее Спасибо за ответ.С Уважением Beksheikh.

    1. Фото аватара Юрий:

      Привет.

      1. Функция getch() используется для захвата одного символа из консоли. Её вам говорили использовать в программах в самом конце, чтобы консольное окно не закрывалось сразу же после выполнения всех действий и чтобы вы успели увидеть результат. return же возвращает код состояния, в случае с return в функции main() — он возвращает 0, если всё произошло без ошибок, всё корректно и 1 (или любой другой символ ненулевой), если были обнаружены ошибки. Детальнее об этом говорится в этом же уроке.

      Функция main() должна возвращать значения, так как она типа int, а не void. return и getch выполняют разные задачи. return должен быть во всех функциях не void. getch() в конце можно прописывать, можно нет. Аналог ему:

      Если у вас консольное окно закрывается сразу же и вы не успеваете увидеть результат выполнения вашей программы — используйте либо getch();, либо system(«pause»);.

      2. Подробный ответ на второй ваш вопрос находится в уроке 24 и в уроке 54.

  30. nurdosramazan:

    Отличный сайт. Автору спасибо
    (даже адблок отключил ;))

    1. Фото аватара Юрий:

      Ахахах спасибо 🙂

  31. Алексей:

    Тут не хватает «{»
    У вас не поставлена=)

    1. Фото аватара Юрий:

      Спасибо, исправил.

Добавить комментарий для илья Отменить ответ

Ваш E-mail не будет опубликован. Обязательные поля помечены *