Quantcast
Viewing all articles
Browse latest Browse all 38

10 Days of Basic Programming, Day 4: functions and scope

Being able to write while loops and if statements is great, it’s most of what you’ll be doing when writing code, but we can’t just write ALL our statements in the same place, that’d be overkill. That’s where functions come in.

A function is a block of code, like an if or a while, that can only be executed when it is explicitely called. A function has a very well defined functionality: it takes in data that needs to be processed in some way and returns the processed data.

In C you always need to declare at least 1 function called main, which tells the compiler where your code starts. A normal definition of a function in C is as follows:
retType name(arguments){
}

There’s three important things to note here:
– retType: this declares the type of the variable that function returns, more on that later.
– name: this is the name of the function.
– arguments: this is a list of arguments the function must recieve in order to operate correctly.

Lets take a look at a common math function:

f(x) = 2x

To use the above function we pass an argument, in this case a number, and replace that argument in the code, perform the operation and return the result:

f(1) = 2*1 = 2

In C, the above function can be easily written as:
int f(int x){
return 2*x;
}

Remember that C is strongly typed, so we have to define the type of the variable returned by the function as well as the type of the arguments, but what about languages that are dynamically typed? Usually they have a reserved keyword for declaring python, for example in Python it is “def”, so the above function would be like this:
def f(x):
return x*2

But, not all functions have to return something, some functions simply take in arguments, do a bunch of stuff with them and return nothing. In C we use the reserved keyword “void”. Void can be defined as “nothing is there”.
Although a void function doesn’t return anything, the “return” statement is not only used to “return a value”, “return” causes the function to inmediately stop. Since this is something we want to be able to do in any function, “return” can be used in void functions, it just can’t have any return value. For example:
void f(int arg){
if (arg != 0){
return;
}
}

But returning is not just the optional part about functions, arguments are only optional, a function can simply have no argument.
Something worth noting is that in some languages like Python, functions always return something, even if not explicitely stated by the programmer. In Python a function that doesn’t return anything ends up implicitely returning None, which is Python’s high-level equivalent of NULL. We’ll learn about what these special values are when we get to pointers.

Now that we know how to create functions we need to learn how to use them.
it seems obvious that to use a function we have to provide it with parameters and do something with the value it returns. Let’s take the first function we created in C:
int f(int x){
return 2*x;
}

We want to know what the value of the function is when we pass value 10 to the argument x and we want to know how to pass arguments. In the case of C and similar languages this is normally done by calling the function by name and passing the parameters between brackets in the same order as they are declared, like this:
f(10);
With the above the function f is called with the value of parameter x as 10 and it returns us 2*10. But perhaps we want to store this returned value. If we just leave that call as is the returned value will be lost. Sometimes we don’t care about the return value and just let it die, but other times it may cause memory problems (which we’ll discuss more in depth with pointers). So how do we store the value returned by a funtion? Well by assigning it to a variable. At this point you should know how variables work: we declare them, we initialize them. We can declare a new variable to store our newly returned value and keep it for later use:
int result = f(10);
The value returned by f() is now stored on our variable result. But, why does the return value get lost if we don’t assign it to our variable?
This is where the term scope comes into play.

Scope is a concept that defines two things: the life of a variable (that is, when the variable no longer exists) and the access of a variable (that is, where the variable can be accessed). But where do scopes exist?
There are two types of scope: global, which means it is accessible anywhere in the code as the variable is defined in the outermost block of code, and local, which is any block of code within the global space.
But what is a block of code exactly? The simplest and easiest way to define a block of code is: a bunch of lines of code grouped together in the same place. When it comes to C, anything being enclosed in braces “{}” is a block of code. An if is a block of code, a while is a block of code, a function is a block of code, there can even be blocks of code for no other reason that to create scopes:
{
lol I'm a plain block of code
}

Evidently, blocks of code can be inside other blocks of code:
void f(){
this is a block of code
if (true){
this is another block of code, inside the previous one
}
}

When we define a variable, memory is reserved for that variable, and it can be accessed by the block of code where the variable was defined in. But when code execution exits a block of code, all the memory taken up by all the variables that were defined in the block is freed up, so the variable can’t be accessed again. This is why child blocks of code can access a parent’s variables: because the parent still hasn’t exited. But on the other hand, a block of code cannot access it’s child’s local variables, because by the time execution returns to the parent block, the child block has finished and so its locals are freed.
The same way, a child block can’t access another child block locals.

This allows for a somewhat automatic memory management: you don’t have to worry about allocating and freeing up memory, you just declare the variables you need and use them how you need, and when you no longer use them, they will be freed. This all works because of the stack.
The stack is a special area in ram reserved for local variables and other temporary data. It is a block-like area with a predetermined maximum capacity, and a pointer that tells you how much data is currently being used, kinda like this:
_
| |
| | <- stack pointer
| |
|_|

Whenever you declare a variable the compiler simply moves the stack pointer down for as many bytes as the variable type needs, when the variable goes out of scope (execution exits the variable’s block of code) the stack pointer is moved up again to where it was. Local variables are not the only thing the stack is used for, but that’s something we won’t be seeing yet.

<<< Previous: 10 Days of Programming, Day 3: Flow Control

>>> Next: 10 Days of Basic Programming, Day 5: containers and declarations

The post 10 Days of Basic Programming, Day 4: functions and scope appeared first on Wololo.net.


Viewing all articles
Browse latest Browse all 38

Trending Articles