Skip to content

A guide to include several files in a C program

During the last week, I’ve been working on a program to manipulate wave files. At one point, it grew too large so I had to divide it into several files. In this post, we’ll see how to divide a single program into several files in C.

We’ll show how to do it based on a simple example program.

Single file program

Let’s work with the following program which includes some type definitions and extra functions.

/* main.c (single file program) */
#include <stdio.h>

/* Type definitions */
typedef int my_type;

struct struct_type {
  my_type val1;
  int val2;
};

union union_type {
  my_type n;
  char c;
};


/* Extra function prototypes */
void special_function(struct struct_type my_struct, union union_type my_union);
void helper_function();


/* Main program */
int main() {
  struct struct_type my_struct = { .val1 = 1 , .val2 = 1};
  union union_type my_union = { .n = 1 };

  special_function(my_struct, my_union);

  printf("Success!\n");
  return 0;
}


/* Extra function definitions */
void special_function(struct struct_type my_struct, union union_type my_union) {
  helper_function();
}


void helper_function() {
  printf("helper function: I'm called by special_function!\n");
}

We’ll divide the different portions of this program into different files so that the main program file will look like this once we’re done:

/* main.c (multiple-file program) */
#include <stdio.h>

/* Extra files headers */
#include "types.h"
#include "special_functions.h"


/* Main program */
int main() {
  struct struct_type my_struct = { .val1 = 1 , .val2 = 1};
  union union_type my_union = { .n = 1 };

  special_function(my_struct, my_union);
  printf("Success!\n");
  return 0;
}

The resultant main file looks much cleaner and it’s a lot easier to grasp what it’s doing. All that needed to be added were include instructions for the extra files.

Let’s see more in detail how to structure those files.

C source code .c and header .h files

First of all, we have to distinguish between two different types of files. Regular code files are .c files. In order to link the code between those files, we associate a header file – .h – to each .c file.

The header files contain only the type definitions and function prototypes (function declarations) that we want to share across different files. These are used to properly determine how the different .c files are linked together before bundling them into a single executable.

Let’s see more in detail how would this look for a specific file.

Type definitions

Our program includes some union and struct type definitions as well as a data type alias using typedef. We’ll write all of them in a single .h file.

/* types.h */
#ifndef TYPES_H
#define TYPES_H

/* Type definitions */
typedef int my_type;

struct struct_type {
  my_type val1;
  int val2;
};

union union_type {
  my_type n;
  char c;
};

#endif

Remember that header files contain only type definitions and function prototypes. In this case, we’ve chosen to have all the type definitions in a single types.h header file.

Include guards

The first two lines and the last line of the header file create an include guard. They are used to avoid importing the same declarations into the program more than once. In practical terms, the portion of code between the #ifndef and the #endif instructions are executed only if the variable TYPES_H isn’t defined (ifndef is short for if not defined). See that the line just after #ifndef will define the TYPES_H variable.

When types.h is first included, TYPES_H won’t be defined. Then, the type definitions will be imported normally. In the process, TYPES_H will also be defined. If the program instructs to include types.h again, we’d haveTYPES_H previously defined, so the type definitions wouldn’t be imported again.

It’s a good practice to use include guards in all of the header files. This is especially important for larger programs where it’s difficult to keep track of what’s been already imported.

External functions

Another portion of our program involved some extra function calls. We’ll separate them from the main program file and have them all in the special_functions.c file and its corresponding special_functions.h file.

First, we include the function prototypes in the header file.

/* special_functions.h */
#ifndef SPECIAL_FUNCTIONS_H
#define SPECIAL_FUNCTIONS_H

/* Extra function prototypes */
void special_function(struct struct_type my_struct, union union_type my_union);

#endif

If you compare it with the main program, you’ll notice that besides the function special_function, there was an additional helper_function. We can get away with not including its prototype in the header file because the helper_function isn’t called by the main() function (it’s called from inside the special_function body).

The special_functions.c will look as:

/* special_functions.c */
#include <stdio.h>

#include "types.h"

static void helper_function();

/* Extra function definitions */
void special_function(struct struct_type my_struct, union union_type my_union) {
  helper_function();
}


static void helper_function() {
  printf("helper function: I'm called by special_function!\n");
}

Some points require extra attention. First, the functions in this file use some user-defined types: struct struct_type and union union_type. Those were defined in the types.h file we saw above. That’s why we include them on the 4th line of code.

Next, we go back to the helper_function. We have this function declared at the beginning of the program so that special_function can call it. A function is “visible” in a file only from the point where it was declared or defined and below.

See also that we’ve added the static keyword before the helper_function declaration. This will have the effect of having this function, helper_function, visible only inside the special_functions.c program. It’s like making it “private” to this file. While this wasn’t strictly necessary for our example, we’ll see an application later on.

Main program file in a multi-file program

Now we can go back to the main function. While we’ve already seen how it would look, let’s give it one extra feature and make it call its own helper_function.

/* main.c */
#include <stdio.h>

/* Extra files headers */
#include "types.h"
#include "special_functions.h"


void helper_function();

/* Main program */
int main() {
  struct struct_type my_struct = { .val1 = 1 , .val2 = 1};
  union union_type my_union = { .n = 1 };

  special_function(my_struct, my_union);
  helper_function();
  printf("Success!\n");
  return 0;
}


void helper_function() {
  printf("helper function: I'm called from main!\n");
}

First, we included the types.h and the special_function.h header files. types.h is required because we have a reference to the user-defined types struct struct_type and union union_type on the main function. We need special_functions.h because it has the declaration of special_function which is called from main.

However, see that this file has its own helper_function declaration. This one is completely different from the helper_function which is defined in the special_functions.c file. Despite both functions having the same name, we will have no name conflict because we declared helper_function in special_functions.c as static (making it private to that file).

Compilation

Our program is now composed of several different files:

  • main.c.
  • types.h.
  • special_functions.c.
  • special_functions.h.

To compile them into a single executable file, we pass all of the .c files to gcc:

$ gcc main.c special_functions.c -o main

The string "main" that comes after the -o flag is used to give a name to the output binary: main. We could use any other name if we wished.

Executing it, we’ll get:

$ ./main 
helper function: I'm called by special_function!
helper function: I'm called from main!
Success!

See that we have two outputs from a helper_function. One that comes from the special_function.c file, where it was declared static, and one from another helper_function that was defined in the main.c program.

With this, we conclude this guide. Feel free to share any comments below.


Further reference:

The files used for this demonstration can be find in this gist.

Published inArticles
Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments