Date: agosto 25, 2025
Author: Guillermo Garcia
Categories: Embedded C programming Tags: Embedded C programming
In this article, we will look at the functions that are a fundamental pillar of C language programs for embedded systems.
Table of Contents
Most computer programs are built to solve more complex problems than the ones we have treated so far. To develop a computer program for complex problems, it is good practice to divide the problem into smaller and more manageable sub-problems or modules. Then, for each of those modules, an algorithm must be written and translated into C code. To support this process of structured programming, C provides the concept of functions.
At least one function is always present in every C program: the main() function! A function in C is a block of code that performs a task and then returns control to a caller. It has its own name and can be called
from different places in the program. As such, functions can also be used to avoid repetition of code.
The C standard library provides a lot of functions that perform common mathematical calculations, string manipulations, input/output, and many other useful tasks. In this section, we will discuss only a few of these libraries. It is worthwhile to look into the full set of standard functions provided.
Like everything in C, functions need to be declared before they can be used in a program. The declarations of standard functions are grouped into header files (one header file per standard library). This is why we have included “” in all previous programs.
This header file contains the declarations of functions like printf(), scanf(), … The function definitions are inserted by the linker that will look for the correct definitions in the corresponding library. For the functions printf(), scanf() for instance, this will be the stdio library.
Overview of the most commonly used functions of some standard libraries will be given.
Functions for string manipulations string.h
int strlen(char *s);
int strcmp(char *s, char *t);
int strncmp(char *s, char *t, int n);
char *strcpy(char *s, char *t);
char *strncpy(char *s, char *t, int n);
char *strcat(char *s, char *t);
char *strncat(char *s, char *t, int n);
char *strchr(char *s, int ch);
Functions for character handling ctype.h
int isalnum(int ch);
int isalpha(int ch);
int iscntrl(int ch);
int isdigit(int ch);
int isgraph(int ch);
int islower(int ch);
int isprint(int ch);
int ispunct(int ch);
int isspace(int ch);
int isupper(int ch);
int isxdigit(int ch);
int tolower(int ch);
int toupper(int ch);
Standard lib functions stdlib.h
double atof(char *s);
int atoi(char *s);
long atol(char *s);
exit(int status);
int system(char *s);
int abs(int n);
long labs(long n);
int rand(void);
Programmers can use self-defined functions to accomplish specific tasks that were not included in the standard libraries. Using self-defined functions is needed to split the problem in sub problems, to avoid code repetition and to be able to reuse code in different programs.
Every function can be described as a black box, that takes inputs or arguments and has a certain result or return value.

Like standard functions, also custom functions need a declaration and a definition. Some declaration needs to be written before the first function call and contains the function name and the types of the return value and parameters or inputs of the function:
return-value-type <function_name>(list of parameter types);
The function definition can be written anywhere in the code. The format of a function definition is:
return-value-type <function_name>(list of parameters + their types)
{
statements;
}
Once the function declaration and definition are written, the function can be called from everywhere in the program. Like standard functions, custom functions are called using the function name followed by parenthesis. If arguments are to be passed on to the function, these arguments are put in between the parenthesis.
Void functions are functions without return value. In its most general form,
a void function without parameters can be described as:
void <function_name>(void);
To indicate that there is no return value, the function name is preceded by the word void. Simply omitting a return-value-type will result in the assumption of an integer return value. As such, the word void before the function name is absolutely necessary to indicate that no return value will be present. In the same way, the word void is used in between the parenthesis following the function name to indicate that no parameters are needed.
In many cases, the caller needs to pass information to the function. This is done by the use of function parameters that are written between parenthesis after the function name. These parameters can then be used as local variables inside the function. In the function definition, every parameter is given a name and type resulting in:
void <function_name>(type par1, type par2, …)
{
statements;
}
The function declaration only contains the types of the different parameters:
void <function_name>(type, type, …);
When the function is called, the parameters in the function definition are replaced by real arguments. Every argument is an expression that is evaluated during the function call. The resulting argument value is then used to initialize the formal parameters.
A function can also produce a result that needs to be given back to the function caller. This function result is called the functionreturn-value and is written before the function name in both the function declaration and the function definition.
Example
Write a program that reads a real base and a non-negative integer exponent and calculates the exponentiation.
#include <stdio.h>
double exponentiation(double, int); /* function declaration */
int main(void)
{
double a, m;
int n;
printf("Exponentiation \n");
printf("Enter base: ");
scanf("%lf%*c", &a);
printf("Enter (non-negative) exponent: ");
scanf("%d%*c", &n);
m = exponentiation(a, n); /* function call */
printf("The result is %16.8f \n", m);
return 0;
}
/* function definition */
double exponentiation(double base, int exponent)
{
double result = 1.0;
int i;
for (i = 0; i<exponent; i++)
{
result *= base;
}
return result;
}
Output:
Exponentiation Enter base: 2 Enter (non-negative) exponent: 3 The result is 8.000000
The statement “return result;” inside the function exponentiation, returns the result to the function main where exponentiation was called. In this example, this result is assigned to the variable m.
Every variable has its own storage class and scope. The storage class indicates how long a variable remains valid while the scope gives information on where the variable can be accessed.
Auto variables have automatic storage duration, meaning that they will be created upon entrance of the block in which they are defined, exist while the block is active and are destroyed when that block is exited.
The scope of these variables is local to the block in which they are defined.
No block outside the defining block has direct access to automatic variables! These variables may be declared with the keyword auto but this is not mandatory. If no storage class is written, the class auto is used as well. As such, if no keyword is added, the variables defined in a function have a scope local to this function!
External variables have static storage duration, meaning that memory will be allocated when the program execution starts and will remain allocated until the program terminates.
An example of external variables are global variables. These variables are created by placing the variable declaration outside any function. In this case, the keyword extern does not need to be added. The variable will be external by default.
The scope of global variables is the entire source code following the declaration. If these variables are not initialized in the declaration, they will be initialized to zero automatically.
The next program demonstrates the difference between local and global variables. Note that, when entering the function, the local variable a belonging to the function has priority over the global variable a. Since the variable b is not declared inside the function, every calculation or assignment done with that variable, refers to the global variable b.
int a, b; /* global variables */
void f(void); /* function declaration */
int main(void)
{
a = 5;
b = 7;
printf("in the main function a = %d and b = %d \n", a, b);
f();
printf("after the function call a = %d and b = %d \n", a, b);
return 0;
}
void f(void)
{
int a; /* local variable, the global var “a” is invisible */
a = 15;
b = 17;
printf("in the function a = %d and b = %d \n", a, b);
}
Output:
in the main function a = 5 and b = 7 in the function a = 15 and b = 17 after the function call a = 5 and b = 17
This is a special case of the storage class auto. If the word register is written in front of a variable declaration, it suggests the compiler to allocate an automatic variable in a high-speed CPU-register, if possible. Of course, this only makes sense if speed is of outmost importance for that variable.
#define LIMIT 1000
int main(void)
{
register int i; /* local variable of type register*/
for (i = 0; i<LIMIT; i++)
{
printf("%8d", i);
}
return 0;
}
Like variables of the storage class extern, also static variables have a static storage duration, meaning that memory will be allocated when the program execution starts and will remain allocated until the program
terminates.
The scope of these variables is often (but not necessary) limited! If a static variable is defined in a block, the scope of that variable is identical to the scope of automatic variables. Therefore, the variable will keep on existing after the execution of the code block, but none of the other code blocks can access that variable. Static variables are initialized only once!
void f(void);
int main(void)
{
f();
f();
f();
return 0;
}
void f(void)
{
static int i = 5; /* static variable (local to function f) */
printf("i = %d \n", i);
i++;
}
Output:
i = 5 i = 6 i = 7

Deja una respuesta