One justification I've seen is that the header defines the interface of a library and allows its implementation to change; the argument reads remarkably like that for pure abstract base classes or what other OOP languages call "interface". The problem, of course, being that C's version of this idea is a bastardized compile-time-only polymorphism.
But from a developer user experience standpoint, what is the difference between having to write things twice for this reason:
// header.h
int foo();
// library.c
int foo() { return 42; }
Versus writing things twice for this reason:
// interface.cs
interface IBar { int foo(); }
// library.cs
class Bar : IBar { int foo() { return 42; } }
Granted in C it was for dumb reasons whereas in a modern language it's for better (?) reasons, but: You're still writing things twice!
But from a developer user experience standpoint, what is the difference between having to write things twice for this reason:
// header.h
int foo();
// library.c
int foo() { return 42; }
Versus writing things twice for this reason:
// interface.cs
interface IBar { int foo(); }
// library.cs
class Bar : IBar { int foo() { return 42; } }
Granted in C it was for dumb reasons whereas in a modern language it's for better (?) reasons, but: You're still writing things twice!