Заголовки - это разделение интерфейса и реализации.
В заголовках - типы данных и опережающие ссылки, а в компилируемых файлах - определения функций.
Когда разрабатывался язык C, он не подразумевал массивных файлов заголовков. Туда выносилось лишь то, без чего совсем жить нельзя. Т.е. это была тонка надстройка над полностью независимыми единицами компиляции, в духе ассемблера.
Например, полные сигнатуры функций не были нужны, достаточно было имён:
my_func();
Возвращаемое значение - либо int, либо указатель. Структуры по значению не возвращались никогда, не было такого механизма. А для массивов его и до сих пор нет. Соответственно, и определений типов в заголовках обычно не требовалось. Каждый параметр - точно так же, размером с указатель, а как его интерпретировать, функция сама разберётся, как и с количеством параметров.
К тому же, модули на C удобнее было интегрировать с модулями на ассемблере, когда в объектном (т.е. скомпилированном) файле - ничего лишнего. Т.е. всё связывание сверх минимально необходимого линкеру - только через заголовки.
Потом из этого начали пытаться лепить язык со строгой типизацией, и заголовки выросли в размерах на несколько порядков. А в C++ типы данных из реинтерпретации указателей вообще превратились в основную сущность программы. Так что основная работа компилятора C++ при сборке проекта - это компиляция бесчисленных заголовков снова и снова. Где-то это решают прекомпиляцией, но всё равно получается долго, и время полной компиляции растёт как минимум квадратично с ростом размера проекта, включая все его внешние зависимости, от которых тоже надо подключать заголовки.
По хорошему, надо было от заголовков сразу и отказаться при попытке делать язык с полноценной системой типов. Но дизайн комитета - это такая штука...