Functions are the way we can structure our code into subroutines that we can:

1. give a name to
2. call when we need them

Starting from your very first program, an “Hello, World!”, you immediately make use of C functions:

#include <stdio.h>

int main(void) {
printf("Hello, World!");
}

The main() function is a very important function, as it’s the entry point for a C program.

Here’s another function:

void doSomething(int value) {
printf("%u", value);
}

Functions have 4 important aspects:

1. they have a name, so we can invoke (“call”) them later
2. they specify a return value
3. they can have arguments
4. they have a body, wrapped in curly braces

The function body is the set of instructions that are executed any time we invoke a function.

If the function has no return value, you can use the keyword void before the function name. Otherwise you specify the function return value type (int for an integer, float for a floating point value, const char * for a string, etc).

You cannot return more than one value from a function.

A function can have arguments. They are optional. If it does not have them, inside the parentheses we insert void, like this:

void doSomething(void) {
/* ... */
}

In this case, when we invoke the function we’ll call it with nothing in the parentheses:

doSomething();

If we have one parameter, we specify the type and the name of the parameter, like this:

void doSomething(int value) {
/* ... */
}

When we invoke the function, we’ll pass that parameter in the parentheses, like this:

doSomething(3);

We can have multiple parameters, and if so we separate them using a comma, both in the declaration and in the invocation:

void doSomething(int value1, int value2) {
/* ... */
}

doSomething(3, 4);

Parameters are passed by copy. This means that if you modify value1, its value is modified locally, and the value outside of the function, where it was passed in the invocation, does not change.

If you pass a pointer as a parameter, you can modify that variable value because you can now access it directly using its memory address.

You can’t define a default value for a parameter. C++ can do that (and so Arduino Language programs can), but C can’t.

Make sure you define the function before calling it, or the compiler will raise a warning and an error:

➜  ~ gcc hello.c -o hello; ./hello
hello.c:13:3: warning: implicit declaration of
function 'doSomething' is invalid in C99
[-Wimplicit-function-declaration]
doSomething(3, 4);
^
hello.c:17:6: error: conflicting types for
'doSomething'
void doSomething(int value1, char value2) {
^
hello.c:13:3: note: previous implicit declaration
is here
doSomething(3, 4);
^
1 warning and 1 error generated.

The warning you get regards the ordering, which I already mentioned.

The error is about another thing, related. Since C does not “see” the function declaration before the invocation, it must make assumptions. And it assumes the function to return int. The function however returns void, hence the error.

If you change the function definition to:

int doSomething(int value1, int value2) {
printf("%d %d\n", value1, value2);
return 1;
}

you’d just get the warning, and not the error:

➜  ~ gcc hello.c -o hello; ./hello
hello.c:14:3: warning: implicit declaration of
function 'doSomething' is invalid in C99
[-Wimplicit-function-declaration]
doSomething(3, 4);
^
1 warning generated.

In any case, make sure you declare the function before using it. Either move the function up, or add the function prototype in a header file.

Inside a function, you can declare variables.

void doSomething(int value) {
int doubleValue = value * 2;
}

A variable is created at the point of invocation of the function, and is destroyed when the function ends, and it’s not visible from the outside.

Inside a function, you can call the function itself. This is called recursion and it’s something that offers peculiar opportunities.