© Mikael Olsson 2019
Mikael OlssonModern C Quick Syntax Referencehttps://doi.org/10.1007/978-1-4842-4288-9_10

10. Functions

Mikael Olsson1 
(1)
Hammarland, Länsi-Suomi, Finland
 

Functions are reusable code blocks that will only execute when called. They allow developers to divide their programs into smaller parts that are easier to understand and reuse.

Defining Functions

A function can be created by typing void followed by the function’s name, a set of parentheses containing another void, and a code block. The first use of the void keyword specifies that this function will not return a value. The second void inside the parentheses means that the function does not accept any arguments.
void myFunction(void) {
  printf("Hello World");
}

Calling Functions

The previous function will print out a text message when it is called. To invoke it, the function’s name is specified followed by an empty set of parentheses.
int main(void) {
  myFunction(); /* "Hello World" */
}

Function Parameters

The parentheses that follow the function’s name are used for passing arguments to the function. To do this, you must first add the corresponding parameters to the function’s parameter list.
void sum(int a, int b) {
  int sum = a + b;
  printf("%d", sum);
}
A function can be defined to take any number of arguments, and they can have any data types. Just ensure the function is called with the same types and number of arguments. In this example, the function accepts two integer arguments and displays their sum.
sum(2, 3); /* "5" */

To be precise, parameters appear in function definitions, while arguments appear in function calls. However, the two terms are sometimes used interchangeably.

Void Parameter

In C, functions that leave out the void keyword from their parameter list are allowed to accept an unknown number of arguments. This is different from C++, where leaving out void means the same as including it: that the function takes no arguments. Therefore, to have the compiler ensure that no arguments are mistakenly passed to a parameterless function, it is necessary in C to include void in the parameter list.
/* Accepts no arguments */
void foo(void) {}
/* Accepts an unknown number of arguments */
void bar() {}

As of C99, the use of an empty parameter list has been deprecated and results in a warning from the compiler.

Return Statement

A function can return a value. The void keyword before the function’s name is then replaced with the data type the function will return, and the return keyword is added to the function’s body followed by an argument of the specified return type.
int getSum(int a, int b) {
  return a + b;
}
Return is a jump statement that causes the function to exit and return the specified value to the place where the function was called. To illustrate, the previous function can be passed as an argument to the printf function since it evaluates to an integer.
printf("%d", getSum(5, 10)); /* "15" */
The return statement can also be used in a void function as a way to exit the function before the end block is reached.
void dummy(void) { return; }
The main function must be set to return an int type, but including an explicit return value is only required in the C89 standard. As of C90 the compiler will automatically add a return statement to the end of the main function if no such statement is present, and with the C99 standard, this implicit return value is guaranteed to be zero.
int main(void) {
  return 0; /* optional */
}

Forward Declaration

An important thing to keep in mind in C is that a function must be declared before it can be called. This can either be achieved by placing the function’s implementation before any references to it, or by adding a declaration of the function before it is called. This kind of forward declaration is known as a prototype and provides the compiler with the information needed to allow the function to be used before it has been defined.
void myFunction(int a); /* prototype */
int main(void) {
  myFunction(0);
}
void myFunction(int a) {}
The parameter names do not need to be included in the prototype; only the data types are required.
void myFunction(int);
In early versions of C, an undeclared function that is referenced is implicitly declared as a function that returns an int and takes an unspecified number of parameters. Relying on this behavior is not recommended and usually results in a warning from the compiler. As of C99 this feature has been removed and will instead result in an error.
int main(void) {
  foo();
}
/* Warning: implicit declaration of foo */
int foo() { return 0; }

Variable Parameter Lists

A function can be defined to accept a variable number of arguments, similar to the printf function. The parameter list of such a function must end with an ellipsis (…) and there must be at least one additional parameter. An int parameter is typically included to let the function know the number of extra arguments that are passed to it.

In the following example, the function accepts a variable number of arguments that are summed up and returned to the caller. To access these arguments the stdarg.h header file is included. This header defines a new type, called va_list, and three functions that operate on variables of this type: va_start, va_arg, and va_end.
#include <stdio.h>
#include <stdarg.h>
int sum(int num, ...) {
  va_list args; /* variable argument list */
  int sum = 0, i = 0;
  va_start(args, num); /* initialize argument list */
  for (i = 0; i < num; i++) /* loop through arguments */
    sum += va_arg(args, int); /* get next argument */
  va_end(args); /* free memory */
  return sum;
}
int main(void) {
  printf("Sum of 1+2+3 = %d", sum(3,1,2,3)); /* 6 */
}

In contrast to C++, C does not allow function overloading or default parameter values. However, variable parameter lists can be used to implement functions that behave in similar ways.

Pass by Value

Variables are by default passed by value. This means that only a copy of the value is passed to the function. Therefore, changing the parameter in any way will not affect the original variable, and passing large variables back and forth can have a negative impact on performance.
#include <stdio.h>
void set(int i) { i = 1; }
int main(void) {
  int x = 0;
  set(x);
  printf("%d", x); /* "0" */
}

Pass by Address

The alternative to passing by value is to use pointer syntax to instead pass the variable by address. When an argument is passed by address, the parameter can be changed or replaced, and the change will affect the original variable.
void set(int* i) { *i = 1; }
int main(void) {
  int x = 0;
  set(&x);
  printf("%d", x); /* "1" */
}
Recall that arrays can be treated as pointers. As such they will automatically be passed by address, as shown in the following example.
void set(int a[]) { a[0] = 1; }
int main(void) {
  int x[] = { 0 };
  set(x);
  printf("%d", x[0]); /* "1" */
}

Return by Value or Address

In addition to passing variables by value or address, a variable may also be returned in one of these two ways. By default a function returns by value, in which case a copy of the value is returned to the caller.
int byVal(int i) { return i + 1; }
int main(void) {
  int a = 10;
  printf("%d", byVal(a)); /* "11" */
}
To instead return by address, the dereference operator is appended to the function’s return type. The function must then return a variable and not an expression or literal, as is allowed when returning by value. The variable returned should never be a local variable since the memory to these variables is released when the function ends. Instead, return by address is commonly used to return an argument that has also been passed to the function by address.
int* byAdr(int* i) { (*i)++; return i; }
int main(void) {
  int a = 10;
  int *p = byAdr(&a);
  printf("%d", *p); /* "11" */
}

Inline Functions

When calling a function it is important to keep in mind that a certain performance overhead occurs. To potentially remove this overhead, the programmer can recommend that the compiler inlines the calls to a specific function by using the inline function modifier. This keyword was added in the C99 standard. It is most suited for use with small functions that are called inside loops, as shown in the following example. Larger functions should not be inlined since this can significantly increase the size of the code, which may instead decrease performance.
inline int increment(int a) { return ++a; }
int main(void) {
  int i;
  for(i = 0; i < 100;) {
    i = increment(i);
  }
}

Note that the inline keyword is only a recommendation. The compiler may—in its attempts to optimize the code—choose to ignore this recommendation, and it may also inline functions that do not have the inline modifier .