Function Arguments and Parameters

Making our functions more useful and dynamic by providing them with additional values to use in their execution
This lesson is part of the course:

Intro to C++ Programming

Become a software engineer with C++. Starting from the basics, we guide you step by step along the way

Free, Unlimited Access
3D art showing two birds having an argument
Ryan McCombe
Ryan McCombe
Edited

We've seen how the execution of our functions can be controlled by conditional statements. These conditional statements allow our functions to do different things each time they are called.

There is another way we can make our functions more flexible. We can pass some additional data to the function whenever we call it. These pieces of data are called the arguments of a function call.

Previously, we had a program like this:

#include <iostream>
using namespace std;

int Health{150};

void TakeDamage(){
  cout << "Inflicting 50 Damage - ";
  Health -= 50;
  cout << "Health: " << Health << '\n';
}

int main(){
  TakeDamage();
  TakeDamage();
  TakeDamage();
}
Inflicting 50 Damage - Health: 100
Inflicting 50 Damage - Health: 50
Inflicting 50 Damage - Health: 0

This function was quite inflexible. Every time we called it, it did 50 damage.

TakeDamage(); // 50 Damage
TakeDamage(); // 50 Damage
TakeDamage(); // 50 Damage

Function Arguments

Instead, what we could do is pass some data into our function, letting the caller choose how much damage needs to be inflicted. That way, we could do a different amount of damage each time the function is called.

That value would be called an argument. We pass an argument between the ( and the ) that we've been using every time we call a function. For example:

TakeDamage(20); // 20 Damage
TakeDamage(50); // 50 Damage
TakeDamage(100); // 100 Damage

To make this work, we also need to update our function to let it accept arguments, and to make use of them.

Function Parameters

When we call a function with an argument, that argument gets passed to a function parameter.

To allow our function to accept an argument, we need to set up a parameter to receive that argument.

We do that between the ( and ) at the top of the function. Similar to a variable, a parameter has a type and a name. The same naming rules and recommendations apply - we should give our parameters meaningful names.

Below, we give our function a parameter of the int type, which we will call Damage:

void TakeDamage(int Damage) {
  Health -= 50;
}

Difference Between Argument and Parameter

The concepts of argument and parameters are very closely related, but are not quite the same thing.

  • An argument is data we provide to a function when we call it
  • A parameter is what receives that data within the function.

Often, these are the same thing, but not always.

For example, we’ll later see how implicit conversions apply to this process, meaning a parameter can have a different type from the argument that it was created from.

There are also more differences we’ll become familiar with as the course progresses.

However, even though "argument" and "parameter" mean slightly different things in the strictest sense, they are also often used interchangeably when code is discussed informally. As such, we should also consider the context in which it's used.

Once we have set up our function to accept an argument, we can then update the body of the function to make use of that data where required. A parameter acts just the same as a variable within our function.

Instead of decreasing Health by 50, we can decrease it by whatever value is stored in the Damage parameter:

void TakeDamage(int Damage) {
  Health -= Damage;
}

Just like that, our code will now work. Our function can now be called with a different Damage value each time:

#include <iostream>
using namespace std;

int Health{150};

void TakeDamage(int Damage){
  cout << "Inflicting " << Damage << " Damage";
  Health -= Damage;
  cout << " - Health: " << Health << '\n';
}

int main(){
  TakeDamage(20);
  TakeDamage(50);
  TakeDamage(70);
}
Inflicting 20 Damage - Health: 130
Inflicting 50 Damage - Health: 80
Inflicting 70 Damage - Health: 10
Test your Knowledge

Functions with a Single Parameter

After running this code, what is the value of Result?

int Square(int x) {
  return x * x;
}

int Result { Square(2) };

Multiple Parameters

By having multiple parameters in our parameter list, our function can receive multiple arguments. We separate multiple parameters using a comma ,

Below, our AddNumbers function has three parameters, all of the int type:

int AddNumbers(int x, int y, int z) {
  return x + y + z;
}

When providing arguments for those parameters when we call our function, we also separate them with a comma:

// This will return 6
AddNumbers(1, 2, 3);

The parameters do not need to match the return type of the function, nor do they need to match the type of other parameters:

int Health { 100 };

void TakeDamage(int Damage, bool isMagical) {
  // Magical damage is doubled
  Health -= isMagical ? Damage * 2 : Damage;
}

TakeDamage(50, true);

Implicit Conversion of Arguments

If we try to pass an argument of the incorrect data type, the compiler will try to do the same implicit conversion technique we saw in previous lessons.

For example, if we try to pass an argument of 5 (an int) into a parameter that has a type of float, the parameter will be the floating point number 5.0.

If we attempt to pass a data type that cannot be converted to what the parameter expects, the code will not compile, and we’ll be notified of the error.

// Float can be converted to int, so this
// will do 25 damage
TakeDamage(25.0f);

// Bool can be converted to int, so this
// will do 1 damage
TakeDamage(true);

// String cannot be converted to int, so
// this will be an error
TakeDamage("Hello!");  

// This will also be an error - we have
// to provide something
TakeDamage();
Test your Knowledge

Functions with Multiple Parameters

After running this code, what is the value of Result?

int Multiply(float x, float y) {
  return x * y;
}

int Result { Multiply(2, 2.5) };

Optional Parameters

In our examples above, we added a second argument to the TakeDamage function to denote whether the damage was magical.

void TakeDamage(int Damage, bool isMagical) {
  // Magical damage is doubled
  Health -= isMagical ? Damage * 2 : Damage;
}

This made our function more flexible but might make it a bit less intuitive to use. Often, we'll want our function to behave a certain way by default, but also to give callers the option to override that default behavior.

For scenarios like this, we can define optional parameters. For example, we can have our function assume that damage is not magical, but still let callers overrule that assumption if they need to:

// This will assumed to be non-magical damage
TakeDamage(50);

// This will override that assumption
TakeDamage(50, true);

To make a parameter optional, we just need to give it a default value in our parameter list. This is the value that the parameter will have if the caller opted not to provide it.

We do this using the = operator within the parameter list. We’ve also moved our parameter list onto a new line. As with most things in C++, we’re free to lay out our code as desired:

void TakeDamage(
  int Damage, bool isMagical = false
) {
  // Magical damage is doubled
  Health -= isMagical ? Damage * 2 : Damage;
}

With this change, callers of our function are free to provide or omit the additional argument as preferred

// This still works, but the false isn't needed
TakeDamage(50, false);

// It is equivalent to this:
TakeDamage(50);

// To override the default parameter:
TakeDamage(50, true);

We can have any number of optional parameters:

void TakeDamage(
  int Damage,
  bool isPhysicalDamage = true,
  bool canBeLethalDamage = true,
) {
  // body here
}

However, we cannot have the required parameters after the optional parameters.

We will see workarounds for this soon, but for now, just note that something like this would be impossible:

void TakeDamage(
  int Damage,
  bool isPhysicalDamage = true,
  bool canBeLethalDamage
) {
  // body here
}

To understand why optional parameters cannot come before required parameters, consider what would happen were we to call this function with an argument list like this:

TakeDamage(50, false);

What are we setting to false here? Is it isPhysicalDamage or canBeLethalDamage? Our intent is not clear. So, the compiler does not allow our function to be set up that way in the first place.

Test your Knowledge

Functions with Optional Parameters

After running this code, what is the value of Result?

int Multiply(int x, int y = 3) {
  return x * y;
}

int Result { Multiply(2) };

After running this code, what is the value of Result?

int Multiply(int x = 2, int y) {
  return x * y;
}

int Result { Multiply(2) };

Explicitly Using a Default Parameter

We’ve seen how we can use a function’s default parameter simply by omitting the argument in the corresponding position.

Alternatively, we can explicitly use a default parameter by passing an empty set of braces, {}, in that position.

int Add(int x = 1, int y = 2, int z = 3){
  return x + y + z;
}

int main(){
  // Will return 6
  Add({}, {}, {}); 
}

This is required if we want to use default parameters in early positions, but provide a value for a later parameter. Below, we use the function’s default values for parameters x and y, but provide a custom value for z:

int Add(int x = 1, int y = 2, int z = 3){
  return x + y + z;
}

int main(){
  // Will return 8
  Add({}, {}, 5);
}

Using Expressions as Arguments

In the previous examples, all our arguments were passed as "literal" values, for example, 25 and true.

In reality, we can use any expression that results in a value of the correct type for the respective parameter, or one that can be converted to that type. For example:

bool isWizard { true };

// This will be equivalent to TakeDamage(50, true)
TakeDamage(25 * 2, isWizard);

Our arguments can also be determined by calls to other functions. As long as that other function returns something of an appropriate type, we can use it:

int CalculateDamage() {
  return 25;
};
TakeDamage(CalculateDamage(), true);

We can even compose functions with themselves:

// Will become AddNumbers(1, 2, 12)
// Which will become 15
AddNumbers(1, 2, AddNumbers(3, 4, 5));
Test your Knowledge

Argument Expressions

After running this code, what is the value of Result?

int Multiply(int x, int y = 3) {
  return x * y;
}

int Result { Multiply(2, Multiply(5)) };

Common Confusion: Parameter Names vs Variable Names

Frequently, the value we want to use in an argument of a function is simply stored in a variable:

int DamageToInflict { 50 };
void TakeDamage(int Damage) {
  // Do stuff
}
TakeDamage(DamageToInflict)

This example is often a point of confusion, as the names do not match up. We created a variable called DamageToInflict and passed it to a function. But the name within the function was different - it was called Damage.

What we call the variable outside the function does not correlate with what we call the parameter. Whilst they might contain the same value, they are two different variables.

Arguments are not mapped to parameters by their name, rather, they are mapped by their order.

When we call a function, the first argument maps to the first parameter; the second argument maps to the second parameter, and so on.

We can see this in the following example:

#include <iostream>
using namespace std;

int x{5};
int y{2};

void SubtractNumbers(int a, int b){
  cout << "\na = " << a
    << "\nb = " << b
    << "\nResult = " << a - b;
}

int main(){
  cout << "Calculating " << x << " - " << y;
  SubtractNumbers(x, y);

  cout << "\n\nCalculating " << y << " - " << x;
  SubtractNumbers(y, x);
}
Calculating 5 - 2
a = 5
b = 2
Result = 3

Calculating 2 - 5
a = 2
b = 5
Result = -3

If these results make sense, we can make things slightly more complicated to solidify the point. Let's rename our function parameters to also be called x and y:

#include <iostream>
using namespace std;

int x{5};
int y{2};

void SubtractNumbers(int x, int y){
  cout << "\nx = " << x
    << "\ny = " << y
    << "\nResult = " << x - y;
}

int main(){
  cout << "Calculating " << x << " - " << y;
  SubtractNumbers(x, y);

  cout << "\n\nCalculating " << y << " - " << x;
  SubtractNumbers(y, x);
}

We now have variables called x and y both outside and inside the function. However, our results are the same:

Calculating 5 - 2
x = 5
y = 2
Result = 3

Calculating 2 - 5
x = 2
y = 5
Result = -3

This is because they're different variables - the x and y outside of our function are not the same as the x and y within our function. They just have the same name. The next lesson in the course is focused on scopes, which discusses this situation in much more detail.

Test your Knowledge

Function Argument Order

After running this code, what is the value of Result?

int x { 1 };
int y { 3 };

int Subtract(int x, int y) {
  return x - y;
}

int Result { Subtract(y, x) };

This concept is quite confusing, so don't worry if this section didn't make sense.

The next lesson explores this topic in more detail and will help build our understanding.

Summary

In this lesson, we explored the fundamental concepts of function arguments and parameters in C++, enhancing our understanding of how to make functions more dynamic and versatile.

Here's a recap of what we've learned:

  • Function Arguments and Parameters: We differentiated between arguments (data passed to a function) and parameters (variables within the function that receive the arguments).
  • Using Arguments in Functions: Demonstrated how to pass various types of data to functions, allowing them to perform different operations based on the provided arguments.
  • Multiple and Optional Parameters: Explored functions with multiple parameters and the use of optional parameters with default values, adding flexibility to function calls.
  • Implicit Conversion of Arguments: Discussed how C++ handles different data types when passed as arguments, including implicit conversion and potential compile-time errors.
  • Argument Expressions: Showed the versatility of arguments by using expressions, including calls to other functions, as arguments.
  • Common Confusions: Addressed a common source of confusion regarding the naming of arguments and parameters, emphasizing their independence and the importance of their order in function calls.

Next Lesson Preview:

In our upcoming lesson, we delve into the concepts of Global and Local Scope.

You'll learn about the scope of variables, how it affects their accessibility within different parts of a program.

Was this lesson useful?

Edit History

  • Content Refresh

  • First Published

Ryan McCombe
Ryan McCombe
Edited
3D art showing a progammer setting up a development environment
This lesson is part of the course:

Intro to C++ Programming

Become a software engineer with C++. Starting from the basics, we guide you step by step along the way

Free, Unlimited Access
Functions, Conditionals and Loops
3D art showing a progammer setting up a development environment
This lesson is part of the course:

Intro to C++ Programming

Become a software engineer with C++. Starting from the basics, we guide you step by step along the way

Free, unlimited access

This course includes:

  • 56 Lessons
  • Over 200 Quiz Questions
  • Capstone Project
  • Regularly Updated
  • Help and FAQ
Next Lesson

Scope

Learn more about how and when we can access variables, by understanding the importance of the scope in which they exist.
3D art of a man using a telescope
Contact|Privacy Policy|Terms of Use
Copyright © 2024 - All Rights Reserved