To share this page click on the buttons below;
Functions and scope
Functions are the bricks a C program is made of: the program itself is a function, the main one which has to be always present in a C program since it is the point our program will start the execution from.
The way the functions are grouped togheter in modules and the relationships among them (i.e. the way the functions are used) make up the architecture of the program. In other words functions are the way you organize your code.
Functions
A function has:
- a name
- a body
- some arguments (but you can have functions which take no argument)
- a return value (but you can have functions without a return value)
To declare a function you provide one behind the other the type of the returned value, the name of the function and the list of arguments (a series of pairs <type,variable name> enclosed in round brackets) and you close the statement with a semicolon.
A function without a returning value returns void
(which means nothing to return), a function that does not use any argument has void
as list of arguments (that again means no arguments).
To define a function you provide the same you provided in the declaration and instead of the semicolon you write the body of your function enclosed in a pair of braces. The body contains what the function does (so it can contain variables, C code and, of course, call to other functions). Once the function has performed its tasks, it returns: that is indicated by the reserved word return
inside the body of the function, if the function has to return something it will be indicated immediately after return
. Pay attention to the fact that as soon as the execution meets the word return
the function execution is stopped and the control is returned to the caller of the function also if the function has still some remaining code after the return statement (that is just to say that the function can return also earlier than the end of the function, but of course we need to know some more about the C language to appreciate how and why this can happen).
The returned value type must obviously match the return type declared at the very beginning of the function. A function that return void
can omit the return
statement, in this case the function returns after its last sentence (or, if you want, when the body of the function reach the closing brace).
To call a function you simply use the function name and put in the list of arguments the variables or the values you want the function processes.
You cannot declare or define a function inside another function in C, you can in other languages, like Python, but not in C.
Functions are stored in memory exactly in the same way used for variables: the space to fit the code of the function is reserved at some address in memory, the name of the function is the human readable form of the address.
The following short program (available here) shows some of the previous concepts and a little more:
#include <stdio.h>
/* program to demonstrate functions */
int global_a = 0;
/* declaration of the function */
int sum(int a, int b);
/* definition of the function */
int sum(int a, int b) {
int c = a + b;
global_a = global_a + 1;
c = a + b;
/* increment the variable a */
a = a + 1;
printf(" (inside sum) - global_a = %d, a = %d\n", global_a, a);
return c;
}
void main(void) {
int d1 = 4;
int d2 = 6;
int result = 0;
printf("\n ---------- function use demonstration ---------- \n\n");
printf(" (1) - value of (main) d1 before the sum = %d\n", d1);
printf(" (1) - value of global_a before the sum = %d\n", global_a);
result = sum(12,45);
printf(" (1) - Result = %d\n", result);
printf(" (1) - value of (main) d1 after the sum = %d\n", d1);
printf(" (1) - value of global_a after the sum = %d\n\n", global_a);
printf(" (2) - value of (main) d1 before the sum = %d\n", d1);
printf(" (2) - value of global_a before the sum = %d\n", global_a);
result = sum(d1,d2);
printf(" (2) - Result = %d\n", result);
printf(" (2) - value of (main) d1 after the sum = %d\n", d1);
printf(" (2) - value of global_a after the sum = %d\n\n", global_a);
printf(" (3) - value of (main) d1 before the sum = %d\n", d1);
printf(" (3) - value of global_a before the sum = %d\n", global_a);
result = sum(global_a,d2);
printf(" (3) - Result = %d\n", result);
printf(" (3) - value of (main) d1 after the sum = %d\n", d1);
printf(" (3) - value of global_a after the sum = %d\n\n", global_a);
}
First the function sum
is declared and is defined.
That function performs the sum of its 2 arguments but it does a little bit more: it increments the first of its arguments a
and it increments the variable global_a
(this variable is defined outside of whatever function, all alone at very beginning of the program). global_a
is a global variable because it is defined outside of any function, while the variables defined inside a function are local to that function.
To perform the sum a local variable c
is used.
The function is then called 3 times (in the main
) with different arguments:
- first with two values (not variables),
- then with 2 variables
d1
andd2
which are both local to themain
function - and then the function is again called but instead if
d1
we use the variableglobal_a
as the first argument.
Once compiled and executed the program shows the following output:
Now let's analyze a little the output of our program.
After the first call the variable d1
remains untouched, this is not surprising, the sum in fact involves two values and variable d1
was not even passed to the function (we say we pass a variable to a function when we use that variable as argument of that function), but the variable global_a
is changed: it is increased by one. global_a
is indeed increased inside the function, but how the function knows about the variable global_a
? Well it turns out that a global variable is available inside the body of every function that sees the declaration of that variable. That is the reason to call that kind of variables global.
What does actually mean that a function sees a global variable? It means that the declaration of the global variable precedes the definition of the function. Here we have two possibilities:
- the global variable is declared inside a C source file (extension .c): then that variable is visible inside every function that is defined below the declaration of the global variable in the same C source file
- the global variable is declared inside a header C file (extension .h): then that variable is visible inside any function defined into a C files (that is usually a C source file, but it can be also another C header file) that includes the C header file that contains the declaration of that global variable.
Now let's analyze the second time we call the function sum
, now we pass the function two variables, the variables d1
and d2
declared locally in the main
function. The function correctly performs the sum of the values contained in d1
and d2
, it increases the variable global_a
and now we know that this happens because global_a
is global, but what about the other increment inside the function? The variable d1
in the main
is passed as the first argument of the function sum
and this argument (which is named a
in the function arguments list) is actually incremented inside the body of sum
. Why the variable d1
remains untouched after the second call?
That happens because the variables defined inside a function (and this is valid also for the variables in the argument list) are local to that function, they cannot be seen outside the function. The function itself is the little world where a local variable lives, nobody from outside knows about their existence. But the d1
local to main
is actually passed to sum
: what happens in this case? It happens that what is passed is just a copy of the original d1
value, which lives in the main
function. We say that function arguments in C language are passed by value (the value is passed not the variable). This explains why d1
is not changed after the second call: a copy of the value of d1
(which is 4) is put in the a
of the sum
function argument, then that copy is actually incremented, but being only a copy, this increment is not brought back to d1
.
When a variable is used into a function call, just a copy of the variable value is passed to the function, so that any change made in the function to that value will not affect the variable used in the call.
We sat arguments function are passed by value.
The third time we call the function we pass the global variable global_a
(which has been incremented twice because of the previous 2 calls of the function). At the end global_a
value is 3. Is this result surprising? Not at all because also in case of global variables the arguments of the function are passed by value
, so that just a unrelated copy of the value is incremented inside the function.
What about the increment of the variable a
inside the function sum
? Is it just useless? Well in this simple example yes, this increments a variable which cannot be used by anyone else, so it is useless here, but the increment does happen (as you can see with the printf inside the function), so that if use the variable a
inside the function after the increment you will use the incremented value. The increment does happen, but what is incremented there is the local variable a
(which is just initialized with the value in the first of its argument at the very beginning of the function call).
global_a
is incremented inside the function because being global it lives outside any function and any function that can see it can change its value directly (as if it was local).
The scope of variables and functions
Where a variable or a function is visible (that means that it can be used, incremented, assigned of whatever other use) it is called the scope of that variable or function.
A global variable is declared outside every functions: its scope is the file in which has been declared or the files which include the header file containing its declaration. Every function that is in the scope can use and manipulate that variable.
A variable is local when it is declare inside a function (both inside the argument list or the body of the function itself). This kind of variables can be manipulated only inside the function: once the function finished all the changes (except for the return value which is copied back) are lost.
The scope of a function is the file in which the function is declared or the files that includes the header files containing the declaration of that function.
As you probably remember from one of the previous chapters we group together functions for a common purpose into a pair of files (a .c source file and a .h header file) that we call module. Some of the functions of a module are declared and defined inside the .c source file and they will not be available outside that file (we keep these functions hidden or private to that file or module), but some others (the ones we want to be used by others) are declared in the correspondent .h header file and only defined in the .c source file. Since the content of the file .h header file is copied wherever that file is included with the preprocessor directive #include <name of the .h file>
. This means that the declaration of those functions are copied in every file that includes that header file. That makes the functions available to other modules (so that the scope of the function is widened and controlled by that include procedure).
The very same happens for global variables, if you declare the global variables into a header file, those variables will be available in every file that includes the header in which the global variable is defined.
These are the first things you need to know about scope, but there is more. There are the reserved words static
and extern
which are able to alter the rules I described above and there is a way a function can access directly the variables that are used as arguments, this will involve a very important concept: the pointers
. We will see all that in the next chapters.
Where declare variables
In first standard ANSI C the variables local to a function have to be declared at the first in the function body, before any line of code that perform some elaboration, so that something like that will be forbidden:
int sum(int a, int b) {
global_a = global_a + 1;
int c = a + b;
c = a + b;
/* increment the variable a */
a = a + 1;
printf(" (inside sum) - global_a = %d, a = %d\n", global_a, a);
return c;
}
because the declaration of c
follows the increment (so an actual elaboration) of global_a
.
But after the coming of C++ (which explicitly refused that rule) there are C compiler (some C dialects) that support the declaration of variables mixed with other statements.
It is worth to remember that variables are available only after the declaration, if you write
int sum(int a, int b) {
global_a = global_a + 1;
int c = a + b;
c = a + b;
/* increment the variable a */
a = a + 1;
printf(" (inside sum) - global_a = %d, a = %d\n", global_a, a);
return c;
}
int global_a;
the compiler will complain that the global_a
inside the function is unknown because its declaration happens after the function.
That brings another point: it is a good practice to always keep the declaration of variables and functions on the top (at the very beginning) of the source files. For example defining a global variable in the middle of a source file because in any case just the functions below that declaration need that global variable is not polite and should be avoided.
Caution with global
Global variables are handy. You can use then in whatever function you want and you don't have to pass them to the function. So that at beginning of the C programming practices, many programmers tend to define a lot of global variables. This, again, is not a good practice. This is due to the fact that the use of global variable disrupt the independence of different software modules, the more the variable is changeable by different functions the less the control you have on that variable, the more the bugs. Try to keep variable local as mush as you can and use global variable only when strictly necessary.
That is just a general suggestion we will see some other techniques that allow to hidden or make available variables and functions in the next chapters.
To share this page click on the buttons below;