A header is a convention generally accepted by C programmers.
It is a usually a .h file which is included into C source files which provides several benefits.
1.- Provides declaration of data types, global variables, constants and functions.
So you don't have to rewrite them time and again. And if they need being changed you just need to change it in a single file.
In example this program composed of two compliation units (two .c files) compiles and runs just fine.
// File funcs.c
#include <stdio.h>
struct Position {
int x;
int y;
};
void print( struct Position p ) {
printf("%d,%d\n", p.x, p.y );
}
// File main.c
struct Position {
int x;
int y;
};
int main(void) {
struct Position p;
p.x = 1; p.y = 2;
print(p);
}
But it is more mantainable to have the declaration for the struct Position
in a header file and just #include
it everywhere it is needed, like this :
// File funcs.h
#ifndef FUNCS_H
#define FUNCS_H
struct Position {
int x;
int y;
};
#endif // FUNCS_H
//File funcs.c
#include <stdio.h>
#include "funcs.h"
void print( struct Position p ) {
printf("%d,%d\n", p.x, p.y );
}
//File main.c
#include "funcs.h"
int main(void) {
struct Position p;
p.x = 1; p.y = 2;
print(p);
2.- Provides some type safety.
C features implicit declaration of functions. A "feature" (or rather an arguable language design mistake) which was fixed in C99.
Consider this program composed of two .c files :
//File funcs.c
#include<stdio.h>
int f(long n)
{
n = n+1;
printf("%ld\n", n );
}
// File main.c
int main(void)
{
f("a");
return 0;
}
With gcc this program compiles without warnings or errors. But does not behave as we could reasonable expect and desire :
jose@cpu ~/t/tt $ gcc -o test *.c
jose@cpu ~/t/tt $ ./test
4195930
Using a header file like this :
//File funcs.h
#ifndef FUNCS_H
#define FUNCS_H
int f(long n);
#endif // FUNCS_H
//File funcs.c
#include<stdio.h>
int f(long n) {
n = n+1;
printf("%ld\n", n );
}
// File main.c
#include"funcs.h"
int main(void) {
f("a");
return 0;
}
The program still compiles and works wrong but at least we get a warning :
jose@cpu ~/t $ gcc -o test *.c
main.c: In function 'main':
main.c:5:5: warning: passing argument 1 of 'f' makes integer from pointer without a cast
f("a");
^
In file included from main.c:2:0:
funcs.h:3:5: note: expected 'long int' but argument is of type 'char *'
int f(long n);
^
jose@cpu ~/t $ ./test
4195930
3.- Provide a public interface while letting the implementation details remain hidden.
When you design your program it is desirable to make it modular. That is to ensure that different parts of it (modules) are as independient as possible. So that when you need to make a change to one module you need not be worried about such change affecting other modules
Headers help in doing this because you put in the header of a modules the data structures, function prototypes and constants that will be needed by the users of such module.
The implementation details go into .c files.
That is how libraries work. The API interface is specified and distributed in header files. But the API code is in .c files which don't need to be distributed. As an user of the library you just need the headers and the compiled library, not its source code.