[NCLUG] A brief note about C and potential successors.

Bob Proulx bob at proulx.com
Sun Jan 19 18:16:16 MST 2020


Brian Sturgill wrote:
> The problem with header files is two fold.
> First, it requires declarations to be placed in two different files.
> The original source and the header, meaning changes must be made in two
> places.

I'm a pedantic person.  So...  In C the header file contains the
external declaration and the body file contains the definition.  There
should be only one definition of any given thing.  Any multiple
declarations must be identical.  Declarations declare and definitions
implement.  Another way to think about things is that definitions
generate assembly language but declarations do not produce anything.

I always start with the header file.  I create the declaration for
whatever it is that I am doing.  Then I copy that into the body file
and change it from an external declaration into a definition.  In the
case of functions I start creating the innards of the function.

I know people who really hate this.  I mean people hate putting in the
closing curly brace and created an entire language based on
indentation to avoid even that!  I just use emacs and I don't find it
trouble but I know that IDEs will "wizard" one through the creation of
a new thing and will put the declarations and definitions in the right
place for you.  So maybe that is the best thing for many, to use an
IDE that will do the right thing automatically.

The advantage of this type of structure is that the compiler can make
one compilation pass (even if it is a two pass comiler) and produce an
object file.  And then the link editor can combine those together to
produce an executable.  It's very efficient.

> Second, sizable projects have numerous nested files, so do operating
> systems, sizable runtimes, etc. The result is large compile times, even for
> trivial files.
> If even one of these files changes, the whole mess has to be read in and
> recompiled.

This sounds like a critism of C++ not of C.  There is no large nexting
problem with C.  However one cannot stop a programmer from using
enough rope to shoot themselves in the foot.

C++ however is infamous for the problem!  Because in C++ due to the
large amount of inlining and templating one often must include a
*HUGE* amount of definition template code in header files and
therefore those header files are often changing content.  It's a basic
design flaw of the language.

Then people tend to make it worse.  I often see programs where body
files have basically one include file.  And that one include file then
includes every possible other one.  That is madness!

And in actuality the C++ programming community's proclivity for
producing this problem in C++ projects was the impetus for Rob Pike
and Ken Thompson and Robert Griesemer creating Go-lang.  They were
annoyed at C++ programs taking literally hours to compile!  Therefore
in Go-lang it is a compile error to include a module that is not
required to be included.  And also to not include a module that is
required.  The problem is avoided there by force of the creator's will
designing a compiler which did not allow sloppiness.

> If you don’t manage your dependencies perfectly, you can get runtime
> failures, because a tiny change in one file didn’t trigger a
> recompile.

I am really surprised to read this.  Because it isn't something I have
even thought about for many years.  The problem was solved many years
ago by having the compiler produce the dependency information itself.

The compiler these days produces the dependency information directly.
I realize this is code bloat for the compiler but it has the
information as it goes along so it isn't much of a problem for it to
print this information out.  Since this information is coming from the
compiler it is correct by design because the compiler is the tool
doing both the compilation and the dependency generation.

I think for trivial hello world type projects it is okay to ignore
this feature chain and do it manually.  I often do.  But for anything
more than extremely simple then using the compiler dependency output
is definitely the way to go.

Bob


More information about the NCLUG mailing list