Functions play a crucial role in structuring code and creating subroutines in the C programming language. This blog post will provide an introduction to C functions, covering their definition, usage, and important aspects.

Overview of C Functions

Functions in C enable us to:

  1. Assign a name to a block of code
  2. Call that block of code whenever needed

Even the simplest C program, like the classic “Hello, World!”, utilizes a function:

#include <stdio.h>

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

Here, the main() function serves as the entry point for the program execution.

Let’s take a look at another example of a C function:

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

Key Aspects of C Functions

C functions possess several important characteristics:

  1. They have a name that allows us to call them later.
  2. They may specify a return value.
  3. They can accept arguments as parameters.
  4. They consist of a body enclosed in curly braces.

The function body comprises the set of instructions executed whenever the function is invoked.

If a function does not return a value, we can use the void keyword before the function name. However, if a return value is expected, we need to specify the corresponding type, such as int for an integer, float for a floating-point value, or const char * for a string.

It is not possible to return multiple values from a single function.

Additionally, functions can have optional arguments. If no arguments are required, we can use void inside the parentheses, like this:

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

In such cases, when calling the function, we simply leave the parentheses empty:

doSomething();

If a function has one parameter, we need to specify its type and name:

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

When calling the function, we pass the corresponding argument within the parentheses:

doSomething(3);

Multiple parameters are separated by commas in both the function declaration and the function call:

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

doSomething(3, 4);

It’s important to note that parameters are passed by copy. This means any modifications made to a parameter within a function only affect its local value. The value originally passed during the function invocation remains unchanged.

However, if we pass a pointer as a parameter, we can directly access and modify the variable’s value using its memory address.

Unlike C++, C does not support default parameter values.

Ensure that you define a function before calling it; otherwise, the compiler will generate 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 indicates an issue with the ordering of the function definition and the function call.

The error arises because, in C, the compiler cannot “see” the function declaration before its invocation. Consequently, it makes assumptions, assuming the function returns int. If the actual return type of the function is void, such as in this case, the error occurs.

By modifying the function definition as follows:

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

we only receive 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, always declare a function before using it. Either move the function declaration up within the code or add a function prototype in a corresponding header file.

Within a function, we can declare variables as needed:

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

These variables are created when the function is invoked and destroyed when the function ends. They are not visible outside of the function.

Moreover, a function can call itself within its body, leading to what is known as recursion. This recursive approach offers unique opportunities in solving problems.

Tags: C functions, structuring code, subroutines, function definition, function parameters, return values, function declaration