Основи програмування на С ++ для початківців

Функції в C++

Пройдя не такой уж долгий путь от нашего первого урока к этому, вы “подошли” к изучению функций в C . Функции – це іменований фрагмент коду, який повторюється в програмі 2 або більше разів .  Коли ми пишемо функцію, необходимо дать ей имя и в дальнейшем, чтобы её вызвать в программе (зmain() або з іншої функції), надо обратиться к ней по этому имени.

Мы уже встречали функции в предыдущих уроках. Это функции для строк (символьных массивов) strlen(), strcmp(), функция для генерации случайных чисел rand(). Ми їх застосовували в програмах і, например, передавали в функцию strlen() строку, а она нам возвращала количество символов в этой строке (ціле число).

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

Да –  Тобто ці чудові стандартні бібліотечні функції, которые мы можем применять в своих программах, але в більшості випадків кожне нове завдання унікально і стандартні функції не завжди підійдуть. В С ++ програміст може самостійно написати власну функцію і застосовувати її з таким же успіхом, как и библиотечные функции.

До певного часу можна обходитися і без функцій. Замість цього плодити однаковий ділянку коду у всій програмі. Но если придется изменить этот код (усовершенствовать или что-то убрать из него), придется вносить изменения по всей программе. Лучше сразу освоить тему функций и активно применять.

Визначити функцію можна двома способами:

    • до main-функции;
  • післяmain-функции. В этом случае необходимо до main-функції оголосити прототип своєї функції.

У цій статті і наступних ми будемо користуватися другим способом, так як він є більш поширеним. Первый способ можно использовать, если функция одна и её код совсем небольшой. Пока мы пишем простые программы, такое встречается часто. Но для программ посложней, будем писать несколько функций которые будут состоять не из 2-3 строк, а побольше. Покажу вам как выглядит определение функции до main():

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

Прототип функции размещен в строке 4, а её определение находится в самом низу программы в строках 20 – 25. Что касается выполнения программы: сначала компилятор прочтет прототип. Это даст ему знать о том, что где-то после main() располагается определение этой функции.

Далі почнеться виконання головної функції main(). Выполняться она будет, поки компілятор не зустріне ім'я функціїourFunctionForPrint(). Тоді він знайде визначення цієї функції, которое расположено после main(), по имени, указанному в прототипе,  виконає її код, после чего снова вернется к выполнению команд main-функции.

В итоге на экране увидим:

функції в с ++, функції c ++

Поговоримо про визначення функцій.

Функції в C ++ можуть не повертати ніяких значень (как в примере) и могут возвращать какое-либо значение. Якщо функція не повертає нічого, то це функція типу void.

Синтаксис функции, которая не возвращает значений:

функции c++, функции в с++

Имя функции следует давать придерживаясь правил для имен переменных. Єдине – желательно чтобы оно содержало глагол, так как функция выполняет действие. Наприклад якщо вона вважає середнє арифметичне можна дати назвуcalculateAverage, если выводит что-то на экран – showText. Имя должно говорить за себя, чтобы не пришлось оставлять лишние комментарии в коде.

Параметри (или аргументы функции) – это данные, які функція приймає і обробляє в тілі. Если функции не нужно ничего принимать для обработки, круглі дужки залишають порожніми. Згідно з правиламиHigh Integrity C Coding Standard    бажано не визначати функції з большім количеством параметров (больше 6).

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

Принимает один параметр:

В 10-й строке кода функция получает параметр – ціле число 7. С ним (с этим числом) произойдет то, что описано в определении функции – строки 16 – 22. А именно – это число подставится в заголовок цикла for. вираз i < questionCount   станет равнозначным i < 7 . В итоге мы увидим на экране 7 знаков вопроса.

функції в с ++, функції c ++

Приймає три параметра:

функції c ++, функції в с ++

Синтаксис функции, которая возвращает значение:

функции в с++, функции c++

Такие функции отличаются тем, что необходимо указать тип значения, которое вернет функция в результате своей работы. Сам возврат значения в программу оформляется оператором return и это значение программа получит в том месте, де функція була викликана . return может возвращать переменную, константу или результат выражения (например: return variable1 - variable2; ).  У тілі функції можуть перебувати кілька операторів return. Тогда, работа функции завершится, якщо спрацює якийсь один з цих операторів. Наприклад:

Определение функции располагается в строках 28 – 34. Если пользователь введет +, сработает блок if в строке 30, а в ньому відповідно спрацює return d1 + d2;  . После этого код функции уже не будет обрабатываться дальше. Компилятор вернется к выполнению main-функции.

функції c ++, функції в с ++

Вы наверное заметили, что в предыдущем коде названия параметров в прототипе и в самом определении функции отличаются от имен переменных, которые передаются в функцию из main.  Справа в наступному –  параметри у визначенні і прототипі функції формальні. Когда мы передаем переменные в виде параметров, функция будет работать не с оригиналами переменных, а з їх точними копіями. Эти копии создаются в оперативной памяти в момент вызова функции. Она работает с этими копиями, а по завершении работы, копии уничтожаются. Так что в прототипах вы можете использовать точные имена переменных, но функция в любом случае будет работать не напрямую с ними, а с их копиями. То есть сами переменные она не может изменить. Коли в наступних уроках ви познайомитеся з указателями і ссылками – узнаете, якомога змінити значення переданих змінних в тілі функції.

Еще немного о прототипе: прочитав его до main, компілятор отримує відомості про те, какой тип возвращаемого значения будет у функции (або вона взагалі не повертає значення – имеет тип void) и о том, какие параметры будут в неё переданы, в каком количестве и в какой последовательности.

прототип int calculateSomeDigits(int d1, int d2, char ч);  говорить компілятору, что функция вернет на место её вызова целое число и о том, що при виклику в неї має бути передано два цілих числа і один символ. При виконанні функції, ми повинні передати їй стільки параметрів скільки вказано в її заголовку при визначенні.

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

Синтаксис прототипа функции:

прототип функції в C ++

Если параметров несколько – вони повинні відокремлюватися комою. Легче всего объявить прототип – это скопировать из определения функции первую строку (заголовок) і після закриває круглої дужки додати крапку з комою.

функції в с ++, функції c ++, прототип функции

Имена переменных-параметров в прототипе можно не указывать. Следующий прототип равнозначен тому, что выше.

функції в с ++, функції c ++, прототип функции

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

Чтобы закрепить то, о чём говорили в этой статье, надо попрактиковаться. Смотрите статью с задачами на функции в C  . В ней вы так же найдете информацию о том, как передавать в функции массивы в виде параметров.  Порада – не просто читайте, а пишите код! Желательно своими силами.

Видео по теме:

24 думки про "Функції в C++

  1. >> Функції - це іменований фрагмента коду, який повторюється в програмі 2 или больше раза .

    Функция – це іменований фрагмент коду. По імені (він же іменований) цей фрагмент може викликатися скільки завгодно раз (навіть нуль раз). У старих книжках писали, що якщо в програмі є фрагмент коду, який повторюється 2 і більше разів, то він претендує на те, щоб стати функцією.

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

  2. >> Коли ми пишемо функцію, необходимо дать ей имя и в дальнейшем, щоб її викликати в main-функції, надо обратиться к ней по этому имени.

    простіше. Функції повинно бути присвоєно ім'я для того, щоб ми могли до неї звернутися (по имени). Звернутися можна звідки завгодно, а не тільки з main().

    Наприклад:
    int foo() { return 1; }
    int bar() { return foo(); } // типа обращаемся к функции не из main
    int main() {
    cout < < bar(); }

  3. Визначити функцію можна двома способами: до main-функції; після main-функції.

    Функція може бути визначена де завгодно, навіть в іншому файлі (не в main.cpp, а скажімо, в foo.cpp).

    Визначення функції включає в себе заголовок (це той шматок, де ми вказуємо значення, що повертається, ім'я функції і типи аргументів) і тіло (це власне код, який виконує функція).

    Але ми можемо оголосити функцію, а тіло їй не написати. Так роблять щоб вказати компілятору що така функція десь є і він не сипав нам помилками. Якщо в кінці кінців функції не виявиться (при складанні програми), Якщо Ви знайшли помилку нам видасть линкер (він займається цією справою).

    Оголошення функції містить лише заголовок.

    коротше, Есслін функція Foo() викликає функцію bar(), це функція бар() повинна бути або оголошена, або визначена за кодом вище ніж foo().

    Наприклад

    ІНТ Foo() { return 1; }
    ІНТ-бар() { повернення Foo(); }
    Чи спрацює без помилок.

    ІНТ-бар() { повернення Foo(); }
    ІНТ Foo() { return 1; }
    Ви отримаєте повідомлення компілятора про те, що функції foo() немає.

    ІНТ Foo(); // це оголошення без реалізації
    ІНТ-бар() { повернення Foo(); }
    ІНТ Foo() { return 1; } // цей код може бути де завгодно (до bar(), після bar() або навіть в іншому файлі
    Чи спрацює без помилок.

  4. >> Если функции не нужно ничего принимать для обработки, круглі дужки залишають порожніми. Згідно з правилами High Integrity C ++ Coding Standard бажано не визначати функції з великою кількістю параметрів(больше 6).

    Ось це правильно. Можна зустріти товаришів, які при відсутності параметрів пишуть void в круглих дужках, так:

    ІНТ Foo(void) { return 1; }
    Це начебто якийсь атавізм від древніх реалізацій Сі-компіляторів. Так робити не треба.

    Про велику кількість параметрів теж вірно, я не читал High Integrity C++ Coding Standard (буду тепер знати що там про 6 аргументів написано), але читав досить популярну в вузьких колах книжку Мартіна “чистий код”. Там він пише, що чим менше параметрів, тим краще. В ідеалі – немає параметрів, але граничним він начебто називає кількість параметрів, рівне трьом. Мартін мотивує це епічно високою складністю тестування функцій з великою кількістю параметрів.

    Я просто трохи доповнив Ваш тезу :).

  5. >> Вы наверное заметили, что в предыдущем коде названия параметров в прототипе и в самом определении функции отличаются от имен переменных, які передаються в функцію з main.

    це, вони можуть відрізнятися, але краще якщо вони збігаються. У прототипі функції імена взагалі можна не писати, так:
    ІНТ Foo(int, float);
    // …
    ІНТ Foo(int a, поплавок б) { повертати + b; }

    Але краще їх все таки писати, т.к. середовище розробки (ваша Visual Studio) підкаже вам тип і ім'я параметрів, коли ви наберете ім'я функції і відкриєте скобочку. Інформацію IDE бере з прототипу. Якщо ви дасте параметрам хороші імена – то віжуал студія зможе вам більш якісно допомагати :)

    1. повністю підтримую – краще, щоб імена змінних та імена параметрів в заголовку функції збігалися. Для себе ми знаємо, що насправді це різні області пам'яті, а при виконанні функції – дійсно реальна допомога.
      З тим, що, краще прописувати імена параметрів в прототипі теж погоджуюся.

      1. У прототипі областей пам'яті взагалі немає. Ви ж написали, що пам'ять виділяється на початку роботи функції і звільняється в кінці. прототип – це просто позначка для компілятора, щоб він не лаявся…

      2. Все вірно. Про різних областях пам'яті – це було про змінних і про параметри в заголовку функції.

  6. все боялася я приступати до цієї теми. Але прочитавши Вашу статтю про функції зрозуміла, що не все так страшно, як здавалося! Спасибі адміну за працю! Я ваш постійний читач тепер :)

  7. До речі в книзі “чистий код” Мартіна цілий розділ присвячений функціям. Я думаю дуже цікаво.
    Наприклад, якщо вірити Мартіну, то функція повинна виконувати рівно одну задачу, а ім'я функції цю задачу чітко описувати. Звідси випливає те, що обсяг функцій повинен бути дуже маленьким (в старих книжках писали що функція повинна вміщатися в 80 строк {на старих моніторах це один екран}, але реально, я вважаю що найчастіше функцію можна і потрібно зробити коротше).

    Ну і всі ці “якщо фрагмент коду повторюється кілька разів…” теж стають не зовсім актуальними при такому підході. просто, якщо фрагмент коду виконує якусь осмислену операцію, то логічно дати цій операції ім'я, загорнувши код в функцію.

    У функцій є невеликий оверхед пов'язаний з викликом функції (і як тут правильно писали, копіюванням параметрів при передачі їх за значенням {як роблять у всіх прикладах цієї статті}), але при правильному підході більшу частину функцій можна оголосити як constexpr і inline, а параметри передавати по посиланню (я вважаю, бажаючі піднімуть літературу :) ).

    1. Programmer_blog, спасибо, що доповнили мою статтю цікавою актуальною інформацією.
      Вам вже колонку треба виділити на нашому сайті, для коментування статей )

залишити коментар

Ваша електронна адреса не буде опублікований. Обов'язкові поля позначені * *