На предыдущем уроке мы говорили о том, что функция может возвращать значение обратно в caller, используя оператор return. На этом уроке мы узнаем, что такое аргументы в функции и что такое параметры в функции.
Параметры и аргументы функций
Во многих случаях нам нужно будет передавать данные в вызываемую функцию, чтобы она могла с ними как-то взаимодействовать. Например, если мы хотим написать функцию умножения двух чисел, то нам нужно каким-то образом сообщить функции, какие это будут числа. В противном случае, как она узнает, что на что перемножать? Здесь нам на помощь приходят параметры и аргументы.
Параметр функции — это переменная, которая используется в функции, и значение которой предоставляет caller (вызывающий объект). Параметры указываются при объявлении функции в круглых скобках. Если их много, то они перечисляются через запятую, например:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
// Эта функция не имеет параметров void doPrint() { std::cout << "In doPrint()" << std::endl; } // Эта функция имеет один параметр типа int: a void printValue(int a) { std::cout << a << std::endl; } // Эта функция имеет два параметра типа int: a и b int add(int a, int b) { return a + b; } |
Параметры каждой функции действительны только внутри этой функции. Поэтому, если printValue() и add() имеют параметр с именем a
, то это не означает, что произойдет конфликт имен. Эти параметры считаются независимыми и никак не взаимодействуют друг с другом.
Аргумент функции — это значение, которое передается из caller-а в функцию и которое указывается в скобках при вызове функции в caller-е:
1 2 |
printValue(7); // 7 – это аргумент функции printValue() add(4, 5); // 4 и 5 – это аргументы функции add() |
Обратите внимание, аргументы тоже перечисляются через запятую. Количество аргументов должно совпадать с количеством параметров, иначе компилятор выдаст сообщение об ошибке.
Как работают параметры и аргументы функций?
При вызове функции, все её параметры создаются как локальные переменные, а значение каждого из аргументов копируется в соответствующий параметр (локальную переменную). Этот процесс называется передачей по значению. Например:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
#include <iostream> // Эта функция имеет два параметра типа int: a и b // Значения переменных a и b определяет caller void printValues(int a, int b) { std::cout << a << std::endl; std::cout << b << std::endl; } int main() { printValues(8, 9); // здесь два аргумента: 8 и 9 return 0; } |
При вызове функции printValues() аргументы 8
и 9
копируются в параметры a
и b
. Параметру a
присваивается значение 8
, а параметру b
— значение 9
.
Результат:
Как работают параметры и возвращаемые значения функций?
Используя параметры и возвращаемые значения, мы можем создавать функции, которые будут принимать и обрабатывать данные, а затем возвращать результат обратно в caller.
Например, простая функция, которая принимает два целых числа и возвращает их сумму:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
#include <iostream> // Функция add() принимает два целых числа в качестве параметров и возвращает их сумму // Значения a и b определяет caller int add(int a, int b) { return a + b; } // Функция main() не имеет параметров int main() { std::cout << add(7, 8) << std::endl; // аргументы 7 и 8 передаются в функцию add() return 0; } |
При вызове функции add(), параметру a
присваивается значение 7
, а параметру b
— значение 8
. Затем функция add() вычисляет их сумму и возвращает результат обратно в main(). И тогда уже результат выводится на экран.
Результат выполнения программы:
Еще примеры
Рассмотрим еще несколько вызовов функций:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
#include <iostream> int add(int a, int b) { return a + b; } int multiply(int c, int d) { return c * d; } int main() { std::cout << add(7, 8) << std::endl; // внутри функции add(): a = 7, b = 8, значит a + b = 15 std::cout << multiply(4, 5) << std::endl; // внутри функции multiply(): c = 4, d = 5, значит c * d = 20 // Мы можем передавать целые выражения в качестве аргументов std::cout << add(2 + 3, 4 * 5) << std::endl; // внутри функции add(): a = 5, b = 20, значит a + b = 25 // Мы можем передавать переменные в качестве аргументов int x = 4; std::cout << add(x, x) << std::endl; // будет 4 + 4 std::cout << add(1, multiply(2, 3)) << std::endl; // будет 1 + (2 * 3) std::cout << add(1, add(2, 3)) << std::endl; // будет 1 + (2 + 3) return 0; } |
Результат выполнения программы:
15
20
25
8
7
6
С первыми двумя вызовами всё понятно.
В третьем вызове аргументами являются выражения, которые сначала нужно обработать. 2 + 3 = 5
и результат 5
присваивается переменной a
. 4 * 5 = 20
и результат 20
присваивается переменной b
. Результатом выполнения функции add(5, 20)
является значение 25
.
Следующая пара относительно лёгкая для понимания:
1 2 |
int x = 4; std::cout << add(x, x) << std::endl; // будет 4 + 4 |
Здесь уже a = x
и b = x
. Поскольку x = 4
, то add(x, x) = add(4, 4)
. Результат — 8
.
Теперь рассмотрим вызов посложнее:
1 |
std::cout << add(1, multiply(2, 3)) << std::endl; // будет 1 + (2 * 3) |
При выполнении этого стейтмента процессор должен определить значения параметров a
и b
функции add(). С параметром a
всё понятно — мы передаем значение 1
(a = 1
). А вот чтобы определить значение параметра b
, нам необходимо выполнить операцию умножения: multiply(2, 3)
, результат — 6
. Затем add(1, 6)
возвращает число 7
, которое и выводится на экран.
Короче говоря:
add(1, multiply(2, 3)) => add(1, 6) => 7
Последний вызов может показаться немного сложным из-за того, что параметром функции add() является другой вызов add():
1 |
std::cout << add(1, add(2, 3)) << std::endl; // будет 1 + (2 + 3) |
Но здесь всё аналогично вышеприведенному примеру. Перед тем, как процессор вычислит внешний вызов функции add(), он должен обработать внутренний вызов функции add(2, 3)
. add(2, 3) = 5
. Затем процессор обрабатывает функцию add(1, 5)
, результатом которой является значение 6
. Затем 6
передается в std::cout.
Короче говоря:
add(1, add(2, 3)) => add(1, 5) => 6
Тест
Задание №1: Что не так со следующим фрагментом кода?
1 2 3 4 5 6 7 8 9 10 11 12 |
#include <iostream> void multiply(int a, int b) { return a * b; } int main() { std::cout << multiply(7, 8) << std::endl; return 0; } |
Задание №2: Какие здесь есть две проблемы?
1 2 3 4 5 6 7 8 9 10 11 12 |
#include <iostream> int multiply(int a, int b) { int product = a * b; } int main() { std::cout << multiply(5) << std::endl; return 0; } |
Задание №3: Какой результат выполнения следующей программы?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
#include <iostream> int add(int a, int b, int c) { return a + b + c; } int multiply(int a, int b) { return a * b; } int main() { std::cout << multiply(add(3, 4, 5), 5) << std::endl; return 0; } |
Задание №4: Напишите функцию doubleNumber(), которая принимает целое число в качестве параметра, удваивает его, а затем возвращает результат обратно в caller.
Задание №5: Напишите полноценную программу, которая принимает целое число от пользователя (используйте std::cin), удваивает его с помощью функции doubleNumber() из предыдущего задания, а затем выводит результат на экран.
Ответы
Чтобы просмотреть ответ, кликните на него мышкой.
Ответ №1
Функция multiply() имеет тип возврата void, что означает, что эта функция не возвращает значения. Но, так как она все равно пытается возвратить значение с помощью оператора return, мы получим ошибку от компилятора. Функция должна иметь тип возврата int.
Ответ №2
Проблема №1: main() передает один аргумент в multiply(), но multiply() имеет два параметра.
Проблема №2: multiply() вычисляет результат и присваивает его локальной переменной, которую не возвращает обратно в main(). А поскольку тип возврата функции multiply() — int, то мы получим ошибку (в некоторых компиляторах) или неожиданные результаты (в остальных компиляторах).
Ответ №3
Функция multiply() принимает следующие параметры: a = add(3, 4, 5)
и b = 5
. Сначала процессор обрабатывает a = add(3, 4, 5)
, т.е. 3 + 4 + 5 = 12
. Затем уже выполняет операцию умножения, результатом которой является 60
. Ответ: 60.
Ответ №4
1 2 3 4 |
int doubleNumber(int a) { return 2 * a; } |
Ответ №5
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
#include <iostream> int doubleNumber(int a) { return 2 * a; } int main() { int a; std::cout<<"Enter a number: "; std::cin >> a; std::cout << doubleNumber(a) << std::endl; return 0; } /* // Следующее решение является альтернативным: int main() { int a; std::cout<<"Enter a number: "; std::cin >> a; a = doubleNumber(a); std::cout << a << std::endl; return 0; } */ |
Примечание: У вас могут быть и другие решения заданий №4 и №5 — это ок. В программировании есть много случаев, когда одну задачу можно решить несколькими способами.
Вот так написал 4 программу, а потом увидел что это оказалось заданием пятой, спасибо.
Добрый день!
пофантазировал и нафантазировал. Теперь не могу понять в почему не обрабатввется хотя в IDE никаких ошибок не показывает:
а все, разобрался. Зря объявил переменную. Отсюда вопрос: почему так нельзя делать, в чем загвоздка??? что бы понять на будущее. Спс
Если Вы про объявление переменной в теле функций, то они локальные. Область видимости в данном случае только в теле функции. Получается не совсем аргумент, который передали в функцию.
5 задание сделал вот так:
Добрый день, сделал 5 задание вот так:
Меня интересует правильное ли это решение?
Отличнейший урок!
хотелось бы добавить от себя что было бы неплохо добавить в уроке что манёвр add(1, multiply(2, 3)) то есть, поставить вместо аргумента, результат другой функции, работает только с функциями типа int, я сам напоролся на ошибку когда создавал рандомные программы, c функциями типа void этот манёвр не сработает.
И ещё, вопрос который меня сильно интересует, в caller-е когда мы выводим функцию типа void, мы пишем саму функцию и перед ним не пишем std::cout <<, а пишем просто имя функции, круглые скобки, и аргументы внутри скобок, потом обязательно точка с запятой. Так вот, почему в этой строке с выводимым void функцией мы не можем написать std::endl?
Это логично, потому-что тип возврата void не возвращает в caller значения. Если внимательно посмотришь то увидишь что у каждой ф-к в конце есть return a + b; или return a * b; .
Спасибо огромное за ваш труд! Материал воспринимается отлично! Всё очень наглядно, много примеров. Читаю литературу и паралельно ваши уроки. Наверное, для меня это самый лучший способ освоить язык. Поклон и уважение ;))
Пожалуйста)) Очень круто, что Равесли оказался Вам полезным))
Автор этого сайта просто гений.
Спасибо вам большое.
Добрый день, благодарю за прекрасный материал. Я сделала 5е задание вот так:
меня интересует: не будет ли в данном случае int main(int x) ошибкой?
Увы, Elena, использовать таким образом параметры функции main нельзя. Они зарезервированы для передачи аргументов из командной строки (если пока не понимаете, что это такое — не страшно, просто знайте, что main() без параметров — это сокращённая версия, которую можно использовать, когда не нужно передавать аргументы из командной строки). А вам нужно было просто объявить локальную переменную в функции main, то есть перед cin >> x; написать int x;
у меня очень похоже на вашу версию)
Всё же лучше в функции doubleNumber не писать "лишнего": вывод на экран реализовать либо отдельной функцией, либо просто в main. Это связано с тем, что функция должна делать ровно то, что указано в её названии, она не должна выполнять избыточные функции.
можно просто через запятую int add(int a, b) ?
Нет, так как компилятор в данном случае не может вывести тип параметра
b
— необходимо явное указание типа. В отличии, например от случая со стейтментом.Например,
Во многих реализациях компилятора вполне корректен с точки зрения синтаксиса. Но подобный стиль объявления и инициализации переменных не является хорошей практикой в большинстве сообществ.
Как вы генерируете такое количество разнообразный нарисованных монстриков для аватарок? На чью не смотрю — у всех они разные… В чём секрет?
Это встроенная возможность WordPress. Он их сам генерирует рандомно)
Первым делом, Спасибо Огромное за Ваш курс!
Я давно пытался освоить Язык С++ купил, не одну книгу, но так и не ладилось. А тут у Вас всё довольно ясно расписано.
Задание №5 я выполнил немного по другому, не как в вашем ответе.
Так как не вижу смысла забивать функцию main() лишним.
Ну технически такого решения не должно быть. По 4-ому заданию такое усовершенствование функции излишне. Соответственно в 5-ом задании такая функция тоже неуместна. Но за энтузиазм респект)
чувак ты реально крут спасибо за то что ты потратил кучу времени на эти уроки ты нам реально помог
Пожалуйста))
Спасибо за отличные уроки!
И задам глупый вопрос. Пробую ввести все указанные Вами данные в примерах для практики. Вот прям четко по алгоритму,ни шагу в сторону. Идёт уйма ошибок в программе. Ладно,исправляю в силу возможностей. Не всегда получается.
Вопрос: это я настолько тупая? Или так и должно быть?
Может тогда мне чисто теорию почитать лучше.
Никаких ошибок не должно быть вообще, если вы всё делаете по урокам — это 100%. Если вы что-то меняете в коде, настройках своей IDE, используете онлайн-компиляторы, невнимательны или еще что-то — тогда возможны ошибки.
Начинать изучение уроков, если у вас нет опыта, следует с урока №1 ничего не пропуская.
Да простят меня те кто буду это читать)))
Ах ты ж, хитрая жопа! ))
Вопрос по второму заданию.
Решил дописать в конце тела объявления функции multiply следующее: «return a * b;» и мне выдало следующую ошибку: «unused variable 'product'», что можно перевести как «неиспользованная переменная 'product'». Но какая разница какая переменная, ведь возвращаемое значение есть и это "return a * b".
вот отредактированный код, чтобы лучше было видно:
Однако если написать вместо "return a * b;" — "return product", то все сработает как надо.
То есть, объявляя переменную в объявлении функции и присваивая ей определенное значение, нужно вернуть именно ее в caller? Потому что чтобы я не писал в return, мне выдавало ошибку «unused variable 'product'».
Прошу прощения за свою невнимательность, если где то что то пропустил или мои вопросы звучат глупо, я еще в этом пытаюсь разобраться.
Еще раз спасибо за предоставленные материалы.
Проверил твой код, у меня все норм работает, ide — Visual Studio 2019 Community
Скорее всего всё из-за настроек из предыдущих уроков. Если делали всё по урокам, то все Предупреждения(Warnings) распознаются как ошибки. В качестве Warning он ругается, дескать зачем объявили переменную, если не использовали вообще в функции — странно это, батенька. А так как Warning = ошибка при заданных по урокам настройках IDE, то он и не идёт дальше, сообщая об ошибке.
Собственно ничего страшного нет и всё будет работать, но для этого автор и говорил о важности обращать внимания на предупреждения и подчищать хвосты.
По сути ты занял память переменной "product", которую не используешь, поэтому компилятор ругается, критической ошибки нет, просто ты нерационально используешь ресурсы.
Вобще то unused в данном случае говорит о том что переменная product просто не используется и никак не влияет на компиляцию и выполнение программы.
Компилятор просто обращает ваше внимание на то, что у вас есть неиспользуемые переменные и их смело можно "выкинуть" из кода.
Я параллельно вашим урокам начал читать другую литературу по С++. Где-то вычитал, что в идеале функция main() должна лишь вызывать другие функции, не принимая участия в вычислениях. Это верно?
Ну как, и да и нет. Если тебе удобно всё делать через функции — делай) Данный метод хорош при крупных проектах, где каждый программист пишет свою часть кода, а уже после подключают в главной функции.
Читаю и вспоминаю любимую перегрузку функций. Вроде лёгкое явление, но препод постоянно акцентировал внимание на этом, видать больная тема для профессиональных разработчиков.
И почему вы раньше мне не попадались??! От многих заумных фраз других сайтов в состоянии "Взрыв мозга"
Сколько лучше читать уроков в день? Что бы не загружаться лишний раз и запомнить то что я прочитал)
это 4
вот 5
Спасибо вам огромное за бесплатные уроки! Мне очень нравится, всё понятно и в конце каждого урока тест, а это важно. Я до этого немного изучал Java, поэтому мне пока что всё просто. Вот моё решение №5:
Я в шоке, это лучшие уроки что я видел. Очень просто о том, что ранее не вмещалось в башку. Пока все понятно, и тесты реально закрепляют знания. Спасибо!
Пожалуйста 🙂
Спасибо за это замечательное объяснение, подобного нигде не встретил,замечательно!
Пожалуйста)
Вариант выполнения задания №2.
Здравствуйте, я может что то не так делаю, но VS 19 не работает если имя тела программы какое то кроме как int main. int doubleNumber() вот так уже не работает только main? в чем может быть проблема?
В предыдущих уроках это объяснялось, кажется. Функция main — это точка входа в программу. С нее и начинается выполнение программы. Функцию doubleNumber вы объявляете и описываете отдельно, а затем вызываете ее в внутри функции main.
Я все усложнил в 1000 раз, оказывается можно было намного проще код написать. Все верно получилось, но написал вот так:
Чувак спасибо :))
{
благодарочка
}
{
Пожалуйста 😉
}
Выдает ошибка "doubleNumber: функция не принимает 0 аргументов"
Как в поговорке a и b сидели на трубе)))
альтернатива для пятого задания
В задании было сказано: "Напишите функцию doubleNumber()". А вы её написали? Это задание ведь на проверку усвоения урока "Параметры и аргументы функции". Но согласен: путей решения задачи несколько.
Точно также решил
Немного другим путем пошел сразу после прочтения "В программировании есть много случаев, когда одну задачу можно решить несколькими способами."
Подумал, так же и есть, а если…
Я уже дошёл до этой темы и боюсь забыть прошлые темы. Боюсь что на 200 теме буду не помнить какую-нибудь там 30 тему
Без практики в реальных проектах забудешь все 100 тем. Забывать это нормально. Практикующему программисту приходится переодически заглядывать в справочные материалы, что-бы что-то вспомнить.
А можете ли объяснить , я прост не понимаю почему в 3 задание в функции multiply b=5?
А такой вариант плох тем что одна функция выполняет 3 действия?
И еще вопрос, почему code::blocks спокойно выводит в консоль русские буквы без добавления set_locale?
я хз
Один из лучших сайтов где можно выучить C++. Понятно все до мелочей, автору респект!!! Было бы неплохо добавить к примеру видео-уроки)). Еще раз спасибо за ваши старания
Пожалуйста 🙂
Добрый день.
Хотелось бы добавить в данный урок более четкое разграничение функций с параметрами и без. Так как я считаю это важным. Делая задания по данному уроку решил немножко усложнить и столкнулся с проблемой понимания работы функции. То есть функция с параметрами требует аргумента, а функция без параметров нет. Но моя программа не работала… Регулярными ударами головой о проблему решил ее. Было интересно. Посему предлагаю рассмотреть данный вопрос.
И конечно мой код:
Вот тебе,админ, для примера альтернативная программа,ты в ответе предлагал вводить число в консоль,используя функцию main(),я написал альтернативную,где ввод происходит через функцию doubleNumber(),есть ли у такого способа минусы перед твоим,и если да,то какие???Код скомпиллировался,работает как надо.
P.S:
В таком решении ,когда мы в фукции main вызываем функцию doubleNumber(),её аргументом можно прописать произвольное число при этом считать умножаться будет то число,которое мы пропишем в консоли,а не аргумент.Кстати с аргументом "а" в функции doubleNumber код не компиллируется.
Вот беру и смотрю.
Правило хорошего программирования: одна функция должна выполнять одну задачу. (об этом в следующих уроках рассказывается детальнее).
У тебя doubleNumber выполняет две задачи: принимает число и умножает его. Для такой простенькой программы, как эта (это даже не программа — 13 строк кода всего лишь), можно использовать и такой, как у тебя вариант, и такой, как у меня, и другие, которые предлагали в комментариях. В более сложных программах оптимальным вариантом было бы сделать отдельную функцию для получения значения от пользователя и отдельную для умножения значения.
так будет правильнее
У Вас, в примерах к уроку 13 указывается: using namespace std; и дальше везде все равно прописываются std: Корректно ли использование std: в добавок к using namespace std; ? Думаю, что они должны быть взаимоисключающими, я не прав ?
Вы правы. Спасибо за бдительность, исправил.
Сделал через функции, так сказать с заделом на правильность — http://www.onlinegdb.com/B1-Zzq3hM.
Всё правильно и согласно рекомендациям из этого туториала. Радует, что уловили суть и пишите программы с самого начала правильным образом.
Я какой-то не правильный(
У вас тут несколько замечаний (по поводу using namespace std и объявления переменных в глобальной области видимости). Продолжайте читать уроки — постепенно всё узнаете и сами поймете, что у вас не так.
Никак не могу запомнить, как работают параметры. Это решение единственное, которое пришло на ум. Но, как по мне, главное, что оно работает и введённое "a" умножается на два. Но урок я явно не усвоил, так что буду пробовать ещё. Спасибо за такой удобный сайт.
Пожалуйста. По поводу параметров есть не один урок, всё, что нужно — рассматривается.
Спасибо ! Но по мне так лучше пока все поэтапно Но их плюс в том , что их можно повторять много раз .
Для чего костыли с std:: ? Если можно реализовать все быстрее без него с помощью using namespace std;.
Ответ на ваш вопрос в уроках «Конфликт имен и std namespace» и «Using statements».
Все прекрасно! Спасибо Вам! Не думала, что когда-нибудь возьмусь за изучение и что моей головы на это хватит! Написала решение к 5 заданию и какого было мое удивление когда она заработала!))
Никогда не знаешь, пока не попробуешь 🙂
какого пачему твой код сильно похож на мой? рандом наверно
По началу так и делал. Но практика такая не прижилась, как видите 🙂
Нужно добавить на сайте систему (лайк,дизлайк) к коментариям.
Ну это уже в перспективе.
Один из лучших уроков легко и элементарно обьясняющий правило использования параметров в функции. Автору огромное спасибо, даже в книгах для новичков я не встречал раздела про параметры функции.
Спасибо, приятно 🙂