I was just thinking that why I need headers at all? In headers, most oftenly we have the declarations only. In .cpp we have the definitons of that .h file. But, why can't we write all the functions directly in .h file and include it in my project files? Why are we dividing the functions declarations and functions definitions? In header file, we have header guard which prevent to have the definitions again and again. So, there is no point to say that translation unit size would be increased. So, what is the exact reason? Please clarify. Thanks.

Recommended Answers

All 10 Replies

  1. Because if the headers change (software updates, etc), then you would have to update every source file you have.
  2. Header files often have macros defined, global variables that may vary upon the results of macro definitions, etc.
  3. Each header file may include many other headers, which in turn include others, ad infinitum.
  4. Trust me, your idea is not sustainable, period.

Header files also come in very handy if you are working in a team of programmers.
Every programmer can use the definitions in stuff.h and compile his code, without stuff.cpp being implemented already.

commented: Good point! +4

But, why can't we write all the functions directly in .h file and include it in my project files?

Because then linking would fail with a "multiple definitions" error if you include the same header into more than one compilation unit (i.e. if you include it from more than one .c file in the same project).

In header file, we have header guard which prevent to have the definitions again and again.

That will prevent the header being included multiple times into the same compilation unit. It will not prevent different compilation units from including the same header (after all different compilation units need to be able to include the same header).

commented: nice. +4

@rubberman Why will I need to update each and every source file? Can you please elaborate little bit? Please explain more. thanks.

Why will I need to update each and every source file?

For fun, let's use printf as our example even though it's unlikely to change for the forseeable life of C.

int printf(const char *fmt, ...);

int main(void)
{
    printf("Hello, world!\n");
    return 0;
}

You do that in every source file where you use printf, right? Because you don't want to include stdio.h yet still need a declaration. Now the standards committee decides to add another required parameter to printf and you upgrade to a compiler that implements this change. Suddenly your code breaks because printf can't be found, and you must modify the declaration in all of those files (once again, because you didn't want to include stdio.h):

int printf(const char *fmt, int level, ...);

int main(void)
{
    printf("Hello, world!\n", 0);
    return 0;
}

Firstly, In case, if I write all the implementations directly in the header and use it in my project. for example, if I write the <stdio.h> in all my files and if standard committee decides to change thing, then my source would be intact, just I need to change the header file as defintions are there in it only. right? So, why are we dividing the thing in headers(declarations) & cpp (defintions) ? We can write defintions in headers also.

Secondly, In your example, as printf defintions is changed, then we need to change all the usages of the printf() also as you do. In that case also, my code will crash because I am using the old format only. If we can change all the lines where printf is used so as to make our code compatiable, then we can change the declartion also. what's the major use? Thanks.

So, why are we dividing the thing in headers(declarations) & cpp (defintions) ? We can write defintions in headers also.

That introduces the problem of multiple definitions. C doesn't really have an explicit one definition rule like C++, but in practice it's there. You can have as many redundant declarations as you want, but the instant you have two of the same definition in a translation unit, you're borked.

Secondly, In your example, as printf defintions is changed, then we need to change all the usages of the printf() also as you do. In that case also, my code will crash because I am using the old format only. If we can change all the lines where printf is used so as to make our code compatiable, then we can change the declartion also. what's the major use?

I was hoping you'd catch on to that. The answer is yes indeed, though fixing the declarations adds yet another thing you need to remember to change.

Now for the example I really wanted to give originally. Here's what actually happened when C99 changed declarations. Before, printf looked like this:

int printf(const char *fmt, ...);

Afterward, it looked like this:

int printf(const char * restrict fmt, ...);

On top of being a tossup as to whether that will cause compilation errors, how would you know about the change without closely monitoring the C standard?

Why will I need to update each and every source file?

If you only change the definitions (implementations) of the functions, but leave all the declarations (or the function names and signatures) intact, then you won't have to change anything in the rest of the source code. However, and this is a big "however", you will have to recompile every single source file you have. This is really the core of the issue and the main reason for the "separate compilation model" (which is the technical name for this header / source paradigm with each source file being compiled separately and then linked together).

If all your implementation code is in the header files, you create several serious scalability problems for projects using that code. Here are a few points:

1) If headers only contain declarations, then they only need to include other headers in order to create well-defined declarations (function signatures). But if headers also contain the definitions, then they also need to include every header needed for those definitions, which is usually significantly more than what is needed for the declarations alone.

2) If all headers contain all their definitions, then compiling a single source file means that all the code of all the functions must be compiled for that translation unit. So, if any two source files or programs include the same headers, the code in those headers will be compiled twice, which is just a waste of time. If definitions are in source files, they are unique to these source files and only need to be compiled once and linked many times with whichever programs or code uses them. In other words, "compile once, link many times" is much better in the long run than "compile everything every time".

3) If you are distributing a library that is made up of header files containing the function definitions (which is somewhat common in C++, for templated code, they are called "header-only libraries"), then everytime you change anything to the definitions (not even to the declarations), like fixing a small bug or something, then every single piece of code that uses that header (or your library) will need to be re-compiled. If the definitions were in a source file (with headers having only declarations), then you would only need to re-distribute your new compiled binaries (or get people to re-compile them on their own, if you are distributing the source code directly), and none of the user-side would have to be re-compiled, only re-linked with the new binaries (or not even that, if you're using dynamic-link libraries (.dll or .so)).

4) Another important aspect of this header / source paradigm is encapsulation. A header file containing only declarations will only tell the world what can be done with this library, but not how any of those operations are actually done. This is called encapsulation or information hiding, or whatever other academic term you want. The point is, the contract between the users of a library and its implementers is that the implementers must provide code that fulfills the functionalities declared in the headers, and the users must restrict themselves to only relying on those declarations (and whatever behavioral guarantees are associated with them, through API documentation). So, the header file acts as a kind of firewall between the users and the implementers. The implementers can do whatever they want behind that firewall, i.e., inside the source files, as long as they fulfill their end of the bargain. This is made possible by the fact that none of the implementation details in the source file need to bleed out, because all the users need to see is the header file. And point number (3) is also related to this too (encapsulation has great benefits for library maintenance and distribution).

We can write defintions in headers also.

Yes, but you have to take special measures to prevent having problems with the One Definition Rule (ODR) (AFAIK, if it's not explicit in the C standard, all compilers follow the ODR, as defined in the C++ standard). You can add the keyword "static" to make functions invisible after compilation, i.e., they don't get exported or visible to the linker, therefore, not triggering any ODR error (as long as you have header guards, of course). You can add the keyword "inline" (since C99) to tell the linker to ignore the presence of multiple definitions of the same function, by assuming that they are all the same anyway. Or, you can put both keywords, if you want both behaviors too.

Usually, however, using such static or inline functions is only used for small trivial functions or wrapper functions that are only a few lines of code long, where it's more practical to just define them in the header file. It should not be used as a generalized thing that you just put on all functions and write all your functions in the headers... for all the reasons mentioned above.

On top of being a tossup as to whether that will cause compilation errors

OMG, that's why you should never write declarations for functions from another library, not even the standard library. Of course, your example will not cause a compilation error because C does not have name-mangling, and therefore, would not catch any kind of discrepancy between the function signature in your declaration (e.g., without "restrict") versus the function signature of the compiled C library (e.g., with the up-to-date "restrict" keyword). This is a double reason why you should never write declarations for functions from another library in C, because you have no way to verify that the type of the parameters match up. At least, in C++, you'll get a linker error because of type mismatch (although, in this particular case, you'll get a compilation error because "restrict" doesn't exist in C++, unless some compilers accept it as an extension, I'm not sure).

@rubberman Why will I need to update each and every source file? Can you please elaborate little bit? Please explain more. thanks.

If the contents of any of the headers you would have included in your source file(s) changes, then you would have to update each of the affected source files in turn in order to incorporate those changes. For example, structures may have members added or removed, global arrays changed in size, macros that you might use updated, etc, etc, ad nauseum.

A header file makes sense if you take into account future changes to the language.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.