When we write C or Objective-C programs we can divide those programs into smaller blocks of code, each of which performs a certain task. We do this by using a construct called functions.
Table of Contents
What is a Function?
Functions are group of one or more program statements that are collected together into a group and then given a name. Each function is self contained, performs a single coherent task and can be invoked from any number of places within your program i.e. it is reusable.
We can also pass information into the function through the use of function arguments and get information back from a function via the return
keyword (or, as we’ll see later, by modifying the values of the variables we pass in). The value that a function returns via the return
keyword is known as the functions return value.
Functions can either return no value (indicated by the use of the keyword void
) or provide a value of some known data type. Either way, the returned value is available to the calling program at the point the function was originally called.
As we’ve seen in previous posts, all C and Objective-C programs are a collection of one or more functions. There is no limit to the number of functions you can have within your program but all program execution starts and ends with a function called main
and both C and Objective-C programs must therefore contain at least this function.
Once main
starts executing, any additional functions you have created are called in sequence based on the order of the function calls within the main
function. This sequence continues until eventually all the function calls have been made, the main
function exits and your program in turn exits.
Why Do We Use Functions?
So why do we use functions?
The most important reason that we use functions is that it aids us in organizing and understanding our programs. By writing functions, we can design, implement and test our programs in smaller, more manageable chunks. This makes it much easier to understand what is going on in our programs and allows us to build larger, more complex programs by combining a number of individual functions together to perform a more complex task.
In addition to these factors, using functions also helps us reduce the number of lines of code that we need to write.
Suppose we had a particularly complex calculation we needed to perform at several different places within our program. Instead of having to write exactly the same code at each of these different locations, we can write that code once inside a function, and call that function whenever we want to perform the calculation. The code of the function is stored only once in memory but can be used over and over. This make our programs shorter (always a good thing as it reduces the opportunities for making mistakes) and as an added bonus if we subsequently want to change the calculation, we only have to change the code in one place in our program rather than in multiple locations.
With this in mind, any time you have an identical sequence of code statements that you have written more than once within your program, that sequence of statements is a good candidate for being grouped together within their own function.
Using Functions
Just as we learnt in my previous posts on variables, if you want to use a function in your code, you must first tell the compiler about it. There are two main ways to do this:
- Declare the function before it is called.
- Define the function before it is called.
In the next section we’ll look at each of these options.
Declaring a Function
Function declarations, also known as function prototypes, provide the signature or blueprint for a function. The function declaration is the basic information about a function that tells the compiler whether or not a function is being used correctly. A function declaration tells the compiler that at some point later we are going to define a function and also tells the compiler not to worry if it sees that function being called before it’s seen the functions definition.
The syntax of a function declaration is as follows:
return_type function_name(type(1) parameter(1), ..., type(n) parameter(n));
Let’s dissect this a little further.
At the beginning of the declaration we have the return type. The return type, is used to specify the type of data that the function will return.
The return type is the datatype of the result of some calculation that the function has performed but in some situations, a function may not return any information at all. If no information is to be returned the keyword void
is used in place of a datatype to indicate this fact.
Following the return type we have the function name. This is a label that is given to the function so that you can refer to it from elsewhere in your code and allows you to use the functions name when you want the code within the function to be executed.
Following the function name we have an opening, and later a closing, parenthesis. Together these parentheses group together zero or more formal parameters (parameters for short).
Parameters are how we declare the type of information that can be passed into the function and is distinct from the actual information that we pass in which are called arguments.
As a quick aside, if we had a function that calculated the sum of adding two numbers together we might have the function declaration:
int sum(int x, int y);
The two parameters (x
and y
, both of type int
) are the functions parameters. These are different from the values we would use when we actually called the function (such the numbers 20
and 22
in the example below). In this case, the numbers 20
and 22
would be the function arguments:
int result = sum(20, 22);
Anyway, getting back to function parameters. Parameters allow the function to operate using different values or even do different things depending on the information that is passed in. Each parameter is specified as a data type followed by a parameter name (a label given to the particular parameter). If a function accepts multiple parameters each datatype / parameter name pair is separated by a comma. If the function has no parameters, the data type / parameter name pairs are replace with the void keyword, this time to indicate that no information is accepted.
If we turn this into something more concrete and extend our sum example above we would have something like:
#include <stdio.h>
int sum (int x, int y); // Function is declared here....
int main (void) {
int total;
total = sum(20, 22);// … but is used here….
printf("Total is %dn", total);
return 0;
}
int sum (int x, int y) { // …. before we’ve even defined the function here.
return x + y;
}
When the compiler processes a source code file, it does so top to bottom. By having the function prototype at the start of the file (line 3), when the compiler reaches line 7, it is able to check that the sum function is being used correctly by checking the function call against the function prototype.
The function declaration in this case indicates that the sum function has two parameters, both of type int
and will return a single value, also of type int
. Using this information, the compiler checks the statement on line 7.
It sees that two numbers 20
and 22
(both of type int
) are passed into the function as arguments and the result or return value of the function is assigned to the variable total which is of type int
. As the number of arguments and the types of the arguments passed into the function and return value all match the declared function declaration, the compiler confirms that the function is being used correctly and continues to process the source file eventually encountering the function definition at the bottom of the file. We’ll look more at the difference between parameters, arguments and return values later in this post.
Defining a Function
When we come to writing what a function actually does, we refer to this as the functions definition. The function definition must be made at the top level of your program (i.e. a function can’t be defined within another function) and contains the actual code statements that make up the function. There are two main parts to a function definition; the function declarator and the function body.
The Function Declarator
The function declarator is the first line in the function definition. If we took our example above, the function declarator for the sum function would be:
int sum(int x, int y)
The syntax of the function declarator is almost identical to that of the function declaration except there is no semi-colon at the end of the line and it is immediately followed by the function body.
If you have already declared a function, the function declarator, must use the same function name, same parameter types in the same order (assuming the function has parameters) and must also have the same return type as the function declaration. Although it isn’t strictly necessary to have the same parameter names, I’d also recommend that you keep them identical as well.
The Function Body
The function body is whatever is written within opening and closing curly braces ( { }
) of the function. In our sum function above, the body of the function consists of a single program statement but it can be any number of program statements:
return x + y;
Calling a Function
When we want to execute the code in a function we invoke or call the function by using the function name followed by parentheses containing any arguments that we are passing into the function followed by a semi-colon. The syntax of the call is very similar to that of declaration, except that the data types are not used and the parameter names are replaced with the actual values we want to pass into the function. We’ve already seen an example of this in the code above:
result = sum(20, 22);
When we call a function within our code, control of the program is transferred to the function. As part of this process, any arguments you pass into the function are substituted in place of the function parameters and the function starts executing from the first code statement within the function. Once the function is complete (either by encountering a closing curly brace or by executing a return statement), program control returns to the calling program at the point the function was called along with any return value from the function. In our example above, the result of calling the sum function is then assigned to the result variable and execution continues at the next line in our program.
Now, as I’ve hinted at earlier, a function can be called any number of times within the course of a program. For example in the following code snippet we re-use the code in the sum function by calling it multiple times:
#include <stdio.h>
int sum (int x, int y);
int main (void) {
int total1 = sum(20, 22);
int total2 = sum(2, 40);
printf("Total1 is %dn", total1);
printf(“Total2 is %dn”, total2);
return 0;
}
int sum (int x, int y) {
return x + y;
}
Keep in mind that any function can be called from any other function (as long as the calling program is able to see at least a function prototype for the function being called). This includes the ability to call the main()
function from within a function (something I wouldn’t recommend doing in practice) and the ability for a function to call itself (a process called recursion) which can be useful. Recursion is something I might look at in a future post.
Passing Arguments to Functions
As we’ve seen, we have the option of passing information into a function, either to alter how the function operates or as information for the function to process. Where parameters represent the formal definition of what type of information can be passed in and how that information will be referred to within the function (the parameter name) the actual data that is passed is referred to as function arguments.
When you call a function and supply arguments in the function call, the datatypes of the arguments must match the datatypes of the parameters in the function declaration and function definition. The compiler will check your code to ensure that this is the case and when the function is called from your program and if they match, the arguments are substituted in place of the function parameters.
There are essentially two categories of information that you can pass into a function; constants and variables.
Constants are values that do not change. In our example code above, we have been passing in constants into the sum function (for example, the numbers 20
and 22
are both constants) but we also have the option of using variables. We learnt about variables in one of my previous posts. I’ve re-written our example code below, this time using variables:
#include <stdio.h>
int sum (int x, int y);
int main (void) {
int value1 = 20;
int value2 = 22;
int total = sum(value1, value2);
printf("Total is %dn", total);
return 0;
}
int sum (int x, int y) {
return x + y;
}
Returning Values from Functions
As we’ve seen, when we write functions we have the option of returning a value from the function back to the calling program. The returned value is usually the result of the function performing some calculation based on the arguments you have passed in but may be nothing.
To return a value from a function (and exit the function) we use the return
keyword. The return
keyword indicates that the value following the keyword (which might be a constant but is more likely the result of some expression) should be returned and the function should exit. The datatype of the value returned must match that in the function declaration and function declarator.
The syntax of a return statement is as follows:
return (expression);
We’ve seen a concrete example of this already:
return x + y;
By default, functions only have a single return value and if no return type is specified, the return value is assumed to be of type int
. If the function returns no value you must explicitly state this fact by using the keyword void
as the return type in the function declaration and/or declarator. In this case the return statement can then be written as:
return;
So we’ve seen that functions can return a single value back to the calling program, but what if we wanted to return more than one value from a function. This is where using variables as function arguments provides some real power.
Pass-By-Value
In all the examples we’ve seen up until now, we have always passed either constants or the values of variables into a function. When we make function calls in this manner we are said to be passing-by-value. This means that on calling the function we are passing the value of the constant or variable into the function. When we call a function by value, the arguments that are passed into the function are copied at the point the function is called. As the arguments are copied, this means that any changes we make to those values within the function are made to the copies of the variables we pass in rather than to the originals.
To illustrate the point, take a look at the following code example that declares and defines a function called increment
. All this function does is add 1 to the variable argument that is passed in. The output of the program might surprise you though:
#include <stdio.h>
void increment (int x);
int main (void) {
int value = 20;
printf("Before calling increment, the value is: %dn", value);
increment(value);
printf("After calling increment, the value is: %dn", value);
return 0;
}
void increment (int x) {
x = x + 1;
printf("Inside the function the value is: %dn", x);
return;
}
Output:
Before calling increment, the value is: 20
Inside the function the value is: 21
After calling increment, the value is: 20
Pass-By-Reference
There is however, a second method by which we can pass variables into functions, an approach known as pass-by-reference and it is this that frees us from the constraint of returning just a single piece of data from a function.
When we pass a variable into a function by reference, instead of passing a copy of the variables value, we pass the memory address of the original variable. These memory addresses are held in things called pointers.
Pointers are variables that hold a memory address and as such point to some other variable. Pointers are an essential and powerful concept in C and Objective-C programming and we’ll be looking at them a lot in future posts. For now, all you need to know is that in order to make use of these pointers and pass variables into a function by reference instead of by value, we need to make a couple of modifications to our code:
#include <stdio.h>
void increment (int *x);
int main (void) {
int value = 20;
printf("Before calling increment, the value is: %dn", value);
increment(&value);
printf(“After calling increment, the value is: %dn”, value);
return 0;
}
void increment (int *x) {
x = x + 1;
printf("Inside the function the value is: %dn", x);
return;
}
Output:
Before calling increment, the value is: 20
Inside the function the value is: 21
After calling increment, the value is: 21
There are three subtle changes I’ve made to the code listing above.
First I’ve modified the declaration of the increment
function by adding an asterisk (*
) between the data type and the variable name (line 3). This tells the compiler that instead of accepting a variable of type int
, the increment
function will accept a pointer that points to a variable of type int
. I’ve also matched this change in the declarator of the increment
function (line 15).
Note: If you’re having trouble remembering what the asterisk means I’ve found a good way to remember that pointers have an asterisk is to remember that you point to the stars (the asterisk for these purposes)).
The final change I’ve made is in the call to the increment function itself (line 8). Here, instead of simply passing in the value variable as we’ve done previously, I’ve preceded the variable name with the ampersand sign (&
). By adding the ampersand, this means that instead of passing in the value of the variable, we pass in the address of the variable.
Note: A quick tip to remembering how to pass the address of a variable is that both ampersand and address start with the letter ‘A’ (i.e. Ampersand = Address).
By making these modifications, when we manipulate the value of x
within the increment function x
now points to the original value
variable in our main
function. When we change the value of x
inside the increment function we are actually changing the value
variable.
You might not see it just now, but by calling a function by reference we now have a second way of returning values from a function (by modifying the value of a variable that are passed in). This second approach is on top of the simple return
statement we have seen previously and is an approach that isn’t limited to just one function parameter, you can have as many as you like. For now, don’t worry if pass-by-reference is a bit mind-blowing. I’m planning on revisiting it in a future post.
Summary
So as we’ve seen, functions are a powerful tool for you to incorporate into your programs. They provide structure, reduce the amount of code you have to write and make designing and keeping track of how your programs work significantly easier, especially as you start to write larger and more complex programs. We’ve also looked at the concept of parameters and argument (and the differences between them) and seen how we can return information from our functions either by using the return keyword or by passing in variable by reference. Don’t worry pointers and call by reference started to make your head spin. We’ll be visiting it again in future but for now, I just wanted to give you a taste of what is possible. Until next time.
Image source: https://flic.kr/p/8tHxFC