Security-Oriented C Tutorial 0x0D - Functions Part I: Introduction
Welcome to a tutorial explaining functions. This article will help clarify some things we have already been exposed to such as function prototypes, function calls, return values, etc.
A function is a type of routine which has its own defined set of procedures and is practical when you require it to be done in the same exact way more than once. A good example is picking up the phone when someone calls you. As you receive the phone call, the routine of answering the phone is activated and it goes a little something like this.
- Stop what you are currently doing
- Walk to your phone
- Pick up the phone
- Click the button to answer the phone
- Say, "Hello?"
- Listen for a reply
- If it is someone you know, proceed to listen
- Else if it is a telemarketer, hang up the phone
- Wait for the conversation to end
- Click the End Call button
- Put your phone down
- Return to what you were doing before
Going back to tutorial 0x01, Hello, World!, we briefly discussed about the main function. It was described as the entry point for the program so that it knows where to start. The main function is the main routine and from there, we may call other functions such as printf or scanfwhich would run their own subroutine. We can also define our own functions and their own set of instructions so let's learn how to do that.
To declare a function, you must know a few details first. This includes:
- The data you are returning, if it exists
- The name of the function
- The arguments of the function, if they exist
- The instructions to be executed in the function
The following is an example of making a function and it looks pretty similar to declaring a variable.
The function prototype is a means of telling the compiler that there exists a function called add. Without this, when it reaches line 10 of main, it will not know what add and it will start complaining about undefined references. The prototype must have the same return type, name and number of arguments of the correct type as the defined function itself. The argument names need not to be the same, in fact, you may call it anything you want. Whatever name you give it when you define the function it will take the names as variable names. When main calls add and passed in the values of main's num1 and num2, add will declare two variables, also num1 and num2, and initialize them with the corresponding values in the order they were presented. To clarify, main has its own num1 and num2 while add has its own num1 and num2. The variables in either functions are not the same since they have a different scope which means they are not in the same area. I will go explain scope in another tutorial so for now, just understand that if variables are declared within a pair of curly braces, they do not exist anywhere else but in that area. We can change the num1 and num2 in the function definition at the bottom to other names if we wish, it will not change anything since they are just names. One last thing, function prototype arguments do not actually require names, you may just have the data types since the variable names are used in the function itself.
Note that the header files contain function prototypes as well. When header files are included, the entire listing of function prototypes inside those files are copied into the code at compile time. The function definitions for those functions are located elsewhere in libraries. The library which includes the standard C definitions is called libc.
To define the function, all that's needed is the function prototype (without the semicolon) and a pair of curly braces. Inside these curly braces, you are to list the instructions to be executed. In the example code, the only instruction is the return. What happens in the return is the variables num1 and num2 are calculated first before executing the return instruction.
When main calls a function, it will halt at its current position and then jump to the function, saving the next instruction's position so that when it finishes the function it will know where to pick up where it left off. Imagine you are baking a cake and then... the phone rings! You need to stop what you are doing, put everything down and then call the answer phone function (from above). When you finish with the phone, you return to the state you were in before so you pick everything back up and continue baking your cake. This is what happens when a function is called.
When a function has finished its instructions, it needs to return back to main. When a function returns, it may do a couple of things. If your function has a return type like the add function, it will save the return value and pass it back to main. We can see this happening with the variable sum which catches that returned value from add and stores it. It will then proceed to jump execution back to the saved location of the next instruction in the function which called it, in this case, printf in main. If the called function does not have a return value, i.e. its return type is void, it will only do the latter. Once the function has been completed, everything inside it will be collapsed and destroyed.
The function prototyping is actually unnecessary. If you want to make a function without prototyping, you may just define the function above main and all will be well.
Functions are a handy way of dealing with multiple needs of the same set of instructions. It is also great when you need to divide your main function up into smaller, more manageable sections. Just overall, functions are a nice thing to have and use, I like them. Next tutorial, we'll have a quick look at functions in memory.