Static Functions

The static keyworkd in C have different uses. They are all explained in the famous A ‘C’ Test: The 0x10 Best Questions for Would-be Embedded Programmers article written by (Nigel Jones ??):

What are the uses of the keyword static?
(a)    A variable declared static within the body of a function maintains its value between function invocations.
(b)    A variable declared static within a module[1], (but outside the body of a function) is accessible by all functions within that module. It is not accessible by functions within any other module.  That is, it is a localized global.
(c)    Functions declared static within a module may only be called by other functions within that module. That is, the scope of the function is localized to the module within which it is declared.

In this book page I'm going to focus on (c), and give an example of it using our beloved Linked List functions: Let's say that you have functions that you don't want to expose in your .h file (headers file), functions you use internally but you don't see any real application for the user of your library (or may be functions that you use internally and you want to keep as your best kept secret wink, either because they are really really good or because they are shameful crying). In my implementation of the Single List I have a some of them (fortunately not the shameful ones cheeky): create_node(), last_node() and previous_to_node(). These functions I use to simplify my life (aka code-reusing) and I don't see how anybody using my library to create lists would ever want to use them, so I decided to keep it hide by defining them as static within the module (.c file). See below how I define create_node() as static and how I call it from other functions (code reusability).

/* sl_list.c */

#ifndef STDIO_H
    #include <stdio.h>
#endif
#ifndef STDLIB_H
    #include <stdlib.h>
#endif
#include "sl_list.h"
....

static node *create_node(int data)
{
...
}

void push(node **head, int data)
{
    node *curr = create_node(data);
...

}

void enqueue(node **head, int data)
{
    node *new_node = create_node(data);
...

}

...

When I call them from a function within the same module (sl_list.c), there is not a problem, since they are localized to this module, but if I want to invoke them from main.c or from any other module (e.g. dl_list.c), you will get a linker error.

Also, because they are static and localized to the module, you must avoid defining them in the headers file:

/* sl_list.h */

struct list_node {
    int val;
    struct list_node *next;
};

typedef struct list_node node;

void print_list(node *);
void delete_list(node **);
int pop(node **);
void pop_all(node **);
void push(node **, int);
void push_node(node **, node *);
void enqueue(node **, int);
int dequeue(node **);
void dequeue_all(node **);

Now, let's see what error we get when we invoke create_node() from within main.c:

#include <stdio.h>
#include <stdlib.h>
#include "sl_list.h"

int main()
{
   node *head;
   int i;

   head = NULL;
   node *node_sample = create_node(10);

...

 

Compiling source file(s)...
main.c
main.c: In function 'main':
main.c:11:4: warning: implicit declaration of function 'create_node' [-Wimplicit-function-declaration]
node *node_sample = create_node(10);
^
main.c:11:24: warning: initialization makes pointer from integer without a cast [enabled by default]
node *node_sample = create_node(10);
^
main.c:11:10: warning: unused variable 'node_sample' [-Wunused-variable]
node *node_sample = create_node(10);
^
sl_list.c
Linking...
C:\Users\fadelape\Documents\MinGW Studio Projects\linked_list\Debug\main.o: In function `main':
C:\Users\fadelape\Documents\MinGW Studio Projects\linked_list\main.c:11: undefined reference to `create_node'
collect2.exe: error: ld returned 1 exit status

linked_list.exe - 1 error(s), 3 warning(s)

Even If I inadvertely added the definition in the headers file (.h), the linker will not make the liason, as they are defined static and the linker knows we want to keep them secret wink.

/* sl_list.h */

struct list_node {
    int val;
    struct list_node *next;
};

typedef struct list_node node;

void print_list(node *);
void delete_list(node **);
int pop(node **);
void pop_all(node **);
void push(node **, int);
void push_node(node **, node *);
void enqueue(node **, int);
int dequeue(node **);
void dequeue_all(node **);
static node *create_node(int);

Check the error we get:

Compiling source file(s)...
main.c
main.c: In function 'main':
main.c:11:10: warning: unused variable 'node_sample' [-Wunused-variable]
node *node_sample = create_node(10);
^
In file included from main.c:3:0:
main.c: At top level:
sl_list.h:17:14: warning: 'create_node' used but never defined [enabled by default]
static node *create_node(int);
^
sl_list.c
Linking...
C:\Users\fadelape\Documents\MinGW Studio Projects\linked_list\Debug\main.o: In function `main':
C:\Users\fadelape\Documents\MinGW Studio Projects\linked_list\main.c:11: undefined reference to `create_node'
collect2.exe: error: ld returned 1 exit status

linked_list.exe - 1 error(s), 2 warning(s)

This is good, because .h files are always exposed (contrary to .c files that are usually exposed as object files) and this will avoid some intent of hacker to try to guess the header of the function and have access to it (although there are always ways a good hacker... )