Function Return Values in C++

Unlock the ability to have our functions communicate with each other via their return values
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

3D art showing a blacksmith character
Ryan McCombe
Ryan McCombe
Posted

So far, we've been imagining functions as standalone blocks of code. They've been, for the most part, isolated from our surrounding program.

The next step in making our functions more useful will be to allow them to communicate with other parts of our code.

Return values is the main way our functions can send some information to the function that called them. This is done by providing that calling function with some data.

C++ Function Return Types

In our exploration of functions so far, we have waved off the syntax such as void and int that we've been putting before our names:

void TakeDamage() {}
int main() {}

In this lesson, we will see what exactly these are for.

You may have recognised int as a data type that we've seen in other contexts. Indeed, these parts of our syntax are data types.

We've seen that a function can run any code we need. But, functions can also return information to the code that called it.

With C++ being a strongly typed language, we need to specify what type of data our functions will be returning. That is what the int and void in the function headers refer to.

The main function returns an integer, so we have a int in the heading of the function. Our TakeDamage function doesn't return anything, so we placed the type void in that position.

But we haven't been returning an integer from main?

The main function gets some special treatment, given its unique status. That is explained below, under the section on Exit Codes

In the below example, we are initialising a variable called Level but, this time, we are initialising that variable to the result of calling a function:

int Level { CalculateLevel() };

For this to work, the act of calling our CalculateLevel function must result in an integer value.

We can make this happen in two steps - first, we place the int keyword before our function name, to declare that this function is going to return an integer:

int CalculateLevel() {
  // Code here
};

Secondly, we update the body of our function to ensure it actually returns an integer, by using the return keyword:

int CalculateLevel() {
  return 5;
};

With that, our initial line of code will now work, and we're able to set a variable using the return value of a function. After running the following code, Level will have a value of 5.

int CalculateLevel() {
  return 5;
};


int Level { CalculateLevel() };

Test your Knowledge

After running the following code, what will be the value of Health?

float GetHealth() {
  return 100.0;
}
float Health { GetHealth() };

After running the following code, what will be the value of isDead?

bool GetIsDead() {
  return false;
}
bool isDead { GetIsDead() };

Using Function Return Values in C++

We've seen from our TakeDamage function in previous lessons that not all functions need to return something.

In those cases, we have set the return type to be void:

void TakeDamage() {};

However, lets update this function to return something too. For example, it could return the amount of damage that was inflicted:

int TakeDamage() {
  Health -= 50;
  return 50;
}

Now, not only does our function have an effect (ie, reducing Health) - it also returns something, which might be useful in the locations where our TakeDamage function is called:

int Score { 0 };
int main() {
  int DamageInflicted = TakeDamage();

  // We can now use the value returned by this function
  // For example, we might want to update a scoreboard
  Score += DamageInflicted;
}

We could also have implemented the above example in a single statement:

int Score { 0 };
int main() {
  Score += TakeDamage();
}

Note, just because a function returns something, that does not obligate the caller to make use of what is being returned.

If our main function had no use for the return value, but just wanted to call the function because of its other effects, it can just continue to call the function as it did before:

int main() {
  TakeDamage();
}

Test your Knowledge

After running this code, what will be the value of Health?

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

After running this code, what will be the value of NewHealth?

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

After running this code, what will be the value of Health?

int Health { 100 };
int TakeDamage() {
  return Health - 50;
}
TakeDamage();

After running this code, what will be the value of Health?

int Health { 100 };
int TakeDamage() {
  Health -= 50;
  return 50;
}
Health -= TakeDamage();

Multiple Return Statements in C++

When using conditional logic, we may need to have multiple return statements in our code. For example:

bool isDead { true };
int TakeDamage() {
  if (isDead) {
    return 0;
  } else {
    Health -= 50;
    return 50;
  }
}

If our function has a return type, we should make sure that every possible branch through our function results in something of that type being returned.

We cannot return multiple values from a function - a function can only return a single value, of a single type.

Later, we will introduce more complex data types - including things that can be thought of as "containers" - a single object that can contain other objects inside it.

For now though, just treat our functions as being limited to returning a single int, bool or float.

Early Function Return Statements

Something very important to note with functions is that, once they hit a return statement, they immediately stop running and return control back to their caller.

Were we to write our TakeDamage function like in the following example, Health would never be reduced, because the function will always have hit the return statement before hitting line 3.

Line 3 is what is sometimes referred to as "unreachable code". Whilst our code can still work, this often indicates we made a mistake:

int TakeDamage() {
  return 50;
  Health -= 50;  // Unreachable code
}

The first return statement a function encounters is not necessarily the first return statement in its body. As we saw in previous examples, a return statement can be inside conditional blocks such as an if statement.

By combining conditional logic with the property that functions stop once they return, we can make our code a lot more concise. For example, we could reduce this function we saw earlier:

bool isDead { true };
int TakeDamage() {
  if (isDead) {
    return 0;
  } else {
    Health -= 50;
    return 50;
  }
}

To something that does exactly the same thing with less code:

bool isDead { true };
int TakeDamage() {
  if (isDead) return 0;
  Health -= 50;
  return 50;
}

In the above example, the else block has been removed. This is because, if isDead is true, our function will return before any code in the else block would have been reached anyway.

Therefore, having that code within an else statement is redundant.

Test your Knowledge

After running this code, what will be the value of Health?

bool isDead { true };

int getHealth() {
  return isDead ? 0 : 50;
  return 100;
}

int Health { getHealth() }

After running this code, what will be the value of Health?

bool isDead { false };
int getHealth() {
  if (isDead) {
    return 0;
  }
  return 100;
}
int Health { getHealth() };

Common Mistake: return vs cout

A common misunderstanding with beginners, particularly when creating the basic functions typical of introductory courses, is the difference between logging and returning a value from a function.

The following are not equivalent:

void MyFunction() {
  cout << 5;
}
int MyFunction() {
  return 5;
}

If a function isn't working as you expect, particularly issues around return values, ensure you are actually using the return keyword to return the value - not simply logging it out to the console.

Common Mistake: Not Calling The Function

The other common mistake beginners make when trying to use a function is not actually calling it. Rather, they will just refer to the function, such as below:

int GetLevel() {
  return 5;
}
int Level { GetLevel };

This will cause a rather cryptic error message. In the future, we will see scenarios where we want to refer to a function without actually calling it.

For now, if we see any errors around our use of functions, ensure we are calling the function using ():

int GetLevel() {
  return 5;
}
int Level { GetLevel() };

Exit Codes

Something odd about our main function might have caught your attention by this point. Our main function has a return value specified as int, but we haven't been returning an int from it. Lets discuss why this still seems to work.

We should first understand what that the int returning from our main function is supposed to represent. This integer is expected to be an exit code - a number that explains why our program stopped running.

Just like our function can return a value to another function that called it, our entire program can return a value to the program that executed it, such as the operating system.

You may have noticed something like this in your terminal:

The program 'MyProgram.exe' has exited with code 0.

The code 0 here indicates that your main function returned 0. Try returning a different value from main, and see how this output changes.

int main() { return 5; }
The program 'MyProgram.exe' has exited with code 5.

Returning 0 from the main function announces that your program exited as expected, under normal circumstances. In other words, it did not crash.

When using other software, you may have sometimes seen a program crash and then your operating system displaying a popup to give you the option to report the crash. That popup was most likely triggered by a program not returning 0 from its main function.

However, we didn't return 0 (or anything else) from our main function. The reason for this discrepancy, and the reason our code even compiles despite seemingly breaking the rules, is that the main function gets some special treatment.

The C++ specification states:

If control reaches the end of main without encountering a return statement, the effect is that of executing return 0;

Our other functions don't share this property - if they have a return type, they need to return something.

Test your Knowledge

After running this code, what will be the value of Health?

bool isDead { true };
int getHealth() {
  if (isDead) {
    return 0;
  }
}
int Health { getHealth() };

We've seen that if our functions specify a return type, they always need to return something. This includes returning something from every conditional branch, if we are using them.

However, our functions don't necessarily need to return something of the correct type - just something that can be converted to the correct type.

Well discuss this idea in more detail in the next lesson.

Was this lesson useful?

Ryan McCombe
Ryan McCombe
Posted
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

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:

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

Implicit Conversions in C++

Going into more depth on what is happening when a variable is used as a different type
3D art showing a sorcerer character
Contact|Privacy Policy|Terms of Use
Copyright © 2023 - All Rights Reserved