There seems to be some confusion about the nature of the C preprocessor [tt]#include[/tt] directive, and use of the header files.
When you use [tt]#include[/tt], it inserts a copy of the file named after it directly into the text of the source code, at exactly the point where the [tt]#include[/tt] occurs. This means that if I have three files, [tt]foo.h[/tt], [tt]foo.c[/tt], and [tt]bar.c[/tt],
Code: Select all
/* foo.h - global declarations for foo */
#ifndef __FOO__
#define __FOO__ 1
extern int narf;
void foo(int baz, char quux); // prototype for foo()
#endif
Code: Select all
/* foo.c - a function to foo around with */
#include <stdlib.h>
#include "foo.h" // note that user-defined headers use quotes, not angle brackets
int narf;
#include "foo.h" // oops, put it twice by mistake
void foo (int baz, char quux)
{
int bloop;
bloop = atoi(quux);
narf += (baz * bloop);
}
Code: Select all
/* bar.c - a note to follow foo() */
#include "foo.h"
int bar()
{
foo (10, "123");
return (narf / 2);
}
What the compiler actually sees when compiling [tt]foo.c[/tt] and [tt]bar.c[/tt] is:
Code: Select all
int atoi(const char *_s);
void exit(int _status) __attribute__((noreturn));
void free(void *_ptr);
char * getenv(const char *_name);
void * malloc(size_t _size);
void qsort(void *_base, size_t _nelem, size_t _size,
int (*_cmp)(const void *_e1, const void *_e2));
int rand(void);
void * realloc(void *_ptr, size_t _size);
void srand(unsigned _seed);
int system(const char *_s);
extern int narf;
void foo(int baz, char quux);
int narf;
void foo (int baz, char quux)
{
int bloop;
bloop = atoi(quux);
narf += (baz * bloop);
}
Code: Select all
extern int narf;
void foo(int baz, char quux);
int bar()
{
foo (10, "123");
return (narf / 2);
}
(The example [tt]stdlib.h[/tt] was taken from DJGPP; for space reasons, only a small part of the [tt]#include[/tt]d declarations are shown.)
Note that foo.h has the whole header surrounded by a conditional compilation statement, which checks to see if the file has been included in this compilation unit already. If it didn't then the accidental duplicate [tt]#include[/tt] in [tt]foo.c[/tt] would have declared [tt]narf[/tt] and [tt]foo()[/tt] twice, leading to a compilation error.
The role of the header files should now be clearer: it is meant to allow external references to be added automatically to multiple compilations, so that the compiler knows what the types of the external references, and what external linkages it needs to account for in the object code. It is
not used by the linker, nor does it referenced by the compiler directly.
Note that this usage is not enforced; you can put any kind of valid C/C++ code in a header file if you wish. However, it is an extremely bad practice to put anything in a header except global constants, external declarations, and function prototypes, as the purpose is to allow separately compiled functions to be declared consistently in every file that refers to them. It is a good practice to [tt]#include[/tt] a C source file's own header in the source file, to ensure agreement between the prototype and the actual function, but there is nothing that requires you to do it.
The compiler treats the standard libary header files different from user-defined headers in only one way: the standard library headers are [tt]#include[/tt]d using angle brackets around the header name, while user-defined headers have double-quotes around the file name (C++ goes one step further, and omits the file extension on standard headers, to differentiate them from the equivalent C headers).
For further enlightenment, try running cpp (the preprocessor utility) over some program files and review the results.