vdegenne
vdegenne

Reputation: 13270

implicit header includes in C

program.c :

int main () {

    hello();
    return 0;
}

tools.c :

void hello (void) {

    printf("hello world\n");
}

Makefile :

program : program.o tools.o

In the set of files of this program I have no tools.h file, even though it compiles fine with no errors, could someone explain the purpose of header files in C programs ?

For now I only have one idea : variables such as structures are required at the compilation layer...

But in my case, if the header file only contains function prototypes, is it longer required to build it ? (the makefile linker syntax is a bit easier to catch).

Upvotes: 3

Views: 1506

Answers (6)

brice
brice

Reputation: 25029

From the GCC docs:

Header files serve two purposes.

  • System header files declare the interfaces to parts of the operating system. You include them in your program to supply the definitions and declarations you need to invoke system calls and libraries.
  • Your own header files contain declarations for interfaces between the source files of your program. Each time you have a group of related declarations and macro definitions all or most of which are needed in several different source files, it is a good idea to create a header file for them.

Including a header file produces the same results as copying the header file into each source file that needs it. Such copying would be time-consuming and error-prone. With a header file, the related declarations appear in only one place. If they need to be changed, they can be changed in one place, and programs that include the header file will automatically use the new version when next recompiled. The header file eliminates the labor of finding and changing all the copies as well as the risk that a failure to find one copy will result in inconsistencies within a program.

My take is that C headers are legacy artefacts from the early days of C.

It's important to understand that headers are included verbatim into source files by the C preprocessor. So the question really becomes: Why do we need forward declaration?

  • In order to keep executables small, type information is not embedded in the object code generated by C compilers. Because of this, linking against an object file or library requires a definition of the types that each function provides, as this information is not available from the object file. Modern compilers get around this by inspecting the source code or libraries for definitions - Identifiers and signatures are grabbed directly from the source or the library symbols.

  • To keep compilers simple and efficient, it would have been inappropriate to type check definitions from the library they will be linked against. Indeed, it is perfectly valid to compile a file without the used library even being available on the compilation machine. Similarly, it would be onerous to delay the compilation of a type until its dependencies have been compiled, and downright impossible in case of cyclic relationships. This requires that the function signature be made available before the function is used (for type checking). For convenience, C defaults to int fun(...) so that in the then-commonest case, the need to forward-declare functions is lessened.

To make everybody's life easier, manual forward declarations were delegated to the pre-processor. In effect, the C compiler does not have the concept of a header file. Instead, the declarations are organised logically into header files which are then added to the program pre-compilation by the preprocessor.

This saves the programmer from having to type all the needed declarations at the beginning of every compilation unit, but it is essentially what is happening.

All this contortion is really a result of the limitations that existed in the early days of C.

There are, however, some definite advantages that result in these contortions. Having the function definitions available outside the implementation code itself allows clear separation between interface and implementation, which actually allows much cleaner systems to be built. In an ideal world, you would only ever need the header file to use a library, with no prior knowledge of the implementation. (aside: I'll trade you an ideal world for an invisible pink unicorn, if you ever find one.)

Modern languages have higher level constructs to separate implementation form interface: interfaces in Java, duck typing in Python, Protocols in Clojure, contracts in Eiffel, etc...

Upvotes: 0

Tom Quarendon
Tom Quarendon

Reputation: 5708

In C, you are allowed to call a function that has not been declared, it is assumed to be an extern function that returns an int, and the compiler will allow you to pass any number of arguments of any type. Note that this is not recommended.

Header files are necessary to inform the C compiler of the correct signature, particularly the return type. If a declaration exists the compiler will check that the arguments passed match the declaration, although in very old style C only the number of arguments is checked, not their types. Since you can't overload functions in C, I believe that it is valid (though not recommended, and it may not actually work at runtime depending on the calling convention being used) to declare a function :

int hello();

And then actually implement it as :

int hello(char* who) {
    printf("Hello %s\n", who);
}

The linker will link these things together. Note that this is not good style.

Note that this is one of the key changes in C++, you must declare functions before calling them, and will check the types of all arguments.

Upvotes: 1

pmg
pmg

Reputation: 108968

Without a prototype for hello() in scope at the time of call, the compiler assumes (with a warning when properly configured) the prototype is int hello() (note, not int hello(void)).

But the definition does not agree with that prototype: void hello(void) vs int hello() so you've just unleashed Undefined Behaviour. Anything can happen. Specifically, your program may compile and run as you expect it to.

You can avoid that UB by providing a correct prototype, either by writing and including a header file, or by specifying the prototype directly in your program.c source file.

Upvotes: 0

P.P
P.P

Reputation: 121347

In your example, you are compiling both together. So it's fine and you are not getting any error despite missing forward declaraion for "hello".

But there are a many advantages for using header files such as: linking symbols from multiple files easily, avoiding forward declarations in every source file etc.

This greatly avoids multiple declarations all over the place in your source files and mostly importantly reduces the compile time just telling the compiler that the definition is somewhere there in the code.

Upvotes: 0

Alok Save
Alok Save

Reputation: 206508

Header files usually would contain declarations of the functions which are defined in source c files.

What purpose does it serve?

  • It gives you additional safety, the compiler checks the parameters passed to a function against the declaration and reports errors if it finds an discrepancy.
  • They allow seperation of interface from the implementation.Basically, this allows you to provide your code(implementation) as an library, which clients need to link against while just including the interface header file in their applications.

Upvotes: 1

Flynch
Flynch

Reputation: 84

They allow you to share the declaration of functions, variables, structures.. between different c files.

Upvotes: 0

Related Questions