user11224591
user11224591

Reputation:

Why do we only include header files but not source files?

For example:

#include <stdio.h>
//#include <stdio.c>  stdio.c is not needed but why?

int main()
{
    printf("Hello World");
    return 0;
}

To use printf and make the program compile, we add #include <stdio.h>. But we know that .h is just header files and real definition of printf is in stdio.c, so why we don't need to include it like the header as #include <stdio.c>? and how does the linker even produce executable object file without the essential stdio.c being included?

Upvotes: 3

Views: 1939

Answers (4)

th33lf
th33lf

Reputation: 2275

Technically, there is nothing stopping you from #including a source file. The include directive simply copy-pastes the contents of the included file into the including file before the next compilation step. If you were to #include a C file, it would still do the same thing. But then, if you wanted all your code in one translation unit, you wouldn't have split them up into multiple files in the first place. That we #include only header files and not C files is more of a convention than something that the language mandates. Rarely, you can see code out there that #includes C files to handle some special cases.

Coming to the second part of your question, the contents of what you call 'stdio.c' is available already, shipped with your compiler in binary form as the C standard library. The linker implicitly links with the standard library even though you do not specify this when compiling and linking your code.

Upvotes: 5

The linker does not care about any #include's you make. It links the libraries you tell it should link to, normally given as command line arguments. On Linux, not sure about Windows or Mac, you can test this by using a function from math.h. The math functions are usually in libm.so while the other functions of the C-standard are in libc.so. libc.so is linked per default, libm.so is not linked per default. That is why you normally need to add the -lm flag to GCC when you use math functions.

#include <math.h>

int main(void)
{
  //use volatile so GCC does not optimize exp() out
  volatile double v=3;
  return exp(v);
}

If you try to compile and link it with gcc ./main.c -o test you get an error, you need to add the -lm flag. After linking you can check the used shared libraries with readelf -d test | grep lib and you see that the program needs libm.so or libm.so.6 and libc.so or libc.so.6. If you run the program, the libraries will be loaded and used. The definitions of the linked objects are not included in your program when you use dynamic linking (default for libraries).

Upvotes: 2

John Bode
John Bode

Reputation: 123448

how does the linker even produce executable object file without the essential stdio.c being included?

The standard library functions have already been compiled into one or more libraries stored in a well-known location, and those are automatically linked by the compiler.

There are several practical reasons why we typically do not #include source files into each other:

  1. It doesn't scale very well. Real projects can contain hundreds to thousands of distinct functions adding up to several hundred thousand to millions of lines of code. Attempting to process all of that as a single translation unit can result in very long build times (up to several hours, and no that's not an exaggeration), and it's possible for a translation unit to be too large for a compiler to handle. It also means that any change, no matter how minor or localized, requires a full rebuild of the entire project. Incremental builds would not be possible.
  2. You have to be very careful how you manage dependencies between modules - it's easy to wind up with circular dependencies that are difficult to resolve. For example, you have functions in module A that depend on definitions in module B, and functions in module B that depend on definitions in module C, but functions in module C depend on definitions in module A. If you write
    #include "C.c"
    #include "B.c"
    #include "A.c"

    then you'll get an error that the functions in module C are missing stuff from module A. If you write

    #include "A.c"
    #include "C.c"
    #include "B.c"
    

    then the dependency of C on A is resolved, but now A doesn't have the definitions from B that it requires.

    Splitting declarations off into separate .h files helps resolve this problem.

  3. While C doesn't have a concept of "public" vs. "private" vs. "protected", it does allow you to limit the visibility of function and object names to the current source file with the `static` keyword. This is handy for creating helper functions or variables for maintaining state between function calls that aren't visible to the rest of the program. If you include all source files in each other, though, then everything is visible to everything else, which also increases the possibility of name collisions.

Breaking code up into independently compiled modules also makes testing easier, it makes code reuse easier, it makes upgrades easier, etc.

Upvotes: 1

Mark Nunberg
Mark Nunberg

Reputation: 3691

I've come across several projects which actually do #include source files, usually this is to squeeze every last drop of performance to avoid some extra jumps or allowing the compiler to optimize/inline functions, or to do some preprocessor trickery on the "included" source file.

Generally speaking, however, header files contain symbol declarations and structure definitions which may inform the compiler about how functions are to be called and how structure members are arranged in memory.

The actual definition of symbols, as other have mentioned, resides in other objects and are resolved by the linker.

It's important to note that the dynamic linker has two main functions:

  • The link/compile-time linker creates an index of symbols required and metadata related to finding these symbols.
  • The runtime linker, invoked when the application is executed, will locate the relevant objects and map the symbols provided with the symbols required.

That being said, the source code of printf is not required until the application is actually launched.

Upvotes: 2

Related Questions