| Computer Engineering | |
| Index of articles in the Computer Engineering Curriculum | |
| Prereqs | |
| *Science prereqs | |
| *Calc I - derivatives and intergrals | |
| * Electrostatics | |
| 100 level | |
| *Intro to computer engineering | |
| *Intro to programming | |
| *Intro to electricty | |
| *Calc II - limits and series | |
| 200 level | |
| *Linear circuits | |
| *Intro to digital logic | |
| *Intro to Object Oriented Programming with Java | |
| 300 level | |
| *Computer architecture | |
| *Intro to electronic devices | |
| *Programming in C and C++ | |
| 400 level | |
| *Embedded systems | |
| *Networks | |
| *Programming Data Structures and Algorithms | |
| *Signal processing | |
| Electives | |
| *Additional topics in computer programming | |
| This article is part of a series of articles intending to offer a curriculum of Computer Engineering. For information, please see Category:Computer engineering curriculum. |
We already talked a little about functions, but we'll get into some more detail. As we said, a function needs a return type, a name, and a list of parameters. But what is a function really? It's just a block of code somewhere in our program that we can jump into from anywhere else in our program by calling it with it's assigned name.
Contents |
Bake a pie
For a little imagery, remember that a program is a list of instructions for the computer. So imagine you're making a pie, and you're following a recipe from a cookbook, step-by-step. So you cut up some apples, mixed it with cinnamon, and sugar, and nutmeg, and whatever else. Now you need to put that all in the crust, but you don't have a crust! Where do you get the crust? Well the instructions say "See page 83 for pie-crust recipe." So now you mark your spot at the applie-pie-filling recipe, jump to page 83, follow the instructions there in order to make your crust, and then flip back to the original recipe with your newly created pie crust, and pick up where you left off.
That's basically what a program does with a function: it marks it's spot in the code, goes off to whatever function is being called, executes the code there, and then goes back to where it left off, hopefully with something useful being accomplished in the process (like making a pie crust).
Parameters
Unlike our pie crust recipe, functions can also take parameters (also called arguments), as we've discussed briefly. There's lots of different reasons you might take parameters in a function. Remember in our HelloWorld program, the function printf? That took a parameter so it would know what to print.
So what exactly is a parameter? Well, basically just a variable. Parameters have names and types, just like any other variable. To access the value stored in each parameter, the code inside the function just uses the name of the parameter like it would any other variable. The only important difference is what's called the scope. The scope of a variable or function defines what parts of code are able to access the variable or function directly. The scope of a parameter is limited to inside the function. In other words, only the code inside the function is able to directly access the parameters.
So if you can't access a functions parameters, how do you assign them values? Indirectly, through the compiler. When you call a function, you pass parameters to it by making a list of values inside parenthesis following the name of the function. What the compiler does is to take the values you passed in (whether they're literals, stored in variables, or even the output of other functions) and assign them to the parameters for the function to use. Notice that values you pass to a function are RHS (right hand); they supply a value.
Return values
The other important value associated with a function is the return value. The return value of a function is an optional value that can be passed back to whatever code called the function. Like all values, the return value has a type. This is specified through the return type of the function. The function can return a different value each time it's called, but any value it returns must be compatible with the specified return type.
Since functions have an associated value, they can therefore be used on the right hand side of the assignment operator, and in general, any other place a right-hand-side value can be used. Functions are, in fact, RHS. Unlike variables, however, they are not also LHS. A function cannot be assigned a value.
We don't have to do anything with the return value, though. Some functions might be useful just for what they do in their code, not for what they return. In this case, the value just gets discarded by the compiler (or you might here some say that the return value gets "dropped on the floor"). If a function never has anything useful to return, you can explicitly declare this by specifying a return type of void. In this case, you're not allowed to return a value, and the function cannot be used as an RHS. (It still can't be used as LHS, either. You could say it's no-handed).
Declaring and Defining a function
So functions have names and type, just like variables. In fact, they are quite a bit like variables, in a lot of ways. Like a variable, a function represent a location in memory. Instead of a value stored in memory, however, a function represents a block of code stored in memory. Also like a variable, a function must be declared before it can be used. However, whereas the values of a variable doesn't necessarily have before defined being using it (that's where you get grabage from, remember), a function does, so the compiler knows what to execute when it gets called. This isn't, of course, an assignment, because functions can't be LHS. It's just called a definition.
Declaration
The declaration of a function starts out the same as a variable: the type and the function name. After that, we simply specify a list of parameters inside parenthesis, and that's it (don't forget to terminate with a semi colon). It looks like this:
int squareMe(int x);
The return type of the function is int, and the name of the function is squareMe (naming rules for functions are the same as for variables). The function takes one parameter. Notice that parameters are declared the same way as variables, because that's really all they are. So the parameter is of type int, and it's named x.
Definition
To define a function, we simply repeat the declaration, but instead of terminating with a semi-colon we open a code block with a left curly brace, enter our code on the following lines, and terminate the code-block with a right curly-brace. It looks like this:
int squareMe(int x){ return x*x; }
This particular function only has one line, which computes and returns the return value. Note that the return statement can accept any RHS value. In this case, the RHS is the output of an operator, the star-operator. In this case, the star operator just performs a multiplication between the two values.
Calling a function
Now how do we call this function? Easy, just give the name of the function, and pass in RHS values for the parameters in a list inside the parenthesis:
squareMe(15); //will return 255 squareMe(3*4); //passes the result of 3*4, which is 12, so it's the same as squareMe(12), or 144 int x; x = 7; squareMe(x); //pass the value stored in a variable int y; y = squareMe(x); //assign the return value to a variable, y. squareMe(squareMe(7)); //Use the return value of a function as the RHS to pass as a parameter
Note that I used a variable named "x" in the above example, even though the parameter for squareMe is also named x. It's okay though, remember that parameters are scoped only within the function. That means there are actually two different x's, each one representing a different location in memory. Outside of the function, we cannot access the x parameter of the function. Further more, inside the function, we cannot access the variable x from outside the function[1].
Also note that the first two examples "dropped the return value on the floor". The function was called, and the result was computed, but we didn't do anything with it, so it just gets discarded.
Lastly, for the second example, and the last example, notice that the RHS passed as a parameter is a result value. In cases like these, instructions inside the parenthesis are always carried out before calling the function for which the instruction provides a parameter. If they weren't, how would we know what value to pass? Expressions used to generate an RHS for a parameter can be as complex as you want. For instance, we could have used another function to supply a parameter to the inner squareMe, and we could have performed an operation on that functions return value.
Multiple parameters
Let's look at another example, that takes multiple parameters. We use commas to separate parameters in the declaration, the definition, and when calling the function.
//Declaration int squareAndAdd(int a, int b); //Definition int squareAndAdd(int a, int b){ int a_squared = a*a; int b-squared = b*b; return a_squared + b_squared; } //Call the function squareAndAdd(15, 14);
Nested functions
In C++, there's no reason we can't call a function from directly inside another function. Kind of like if the pie-crust recipe that you had jumped to from the pie-filling recipe gave you another recipe to jump to, say for stone-ground-flour. For instance, we could have used our existing function to take care of the squaring down in our squareAndAdd function. That would look like this:
//Calling squareMe from inside another function in squareAndAdd(int a, int b){ int a_squared = squareMe(a); int b_squared = squareMe(b); return a_squared + b_squared; }
However, what we can't do in C++ is to declare or define a function inside another function. Some languages allow this, but C++ does not.
