Här kommer ungefär samma förklaring som alla andra sagt innan, men kanske en något annan vinkel

.h-filen deklarerar, talar om vad som finns i C-filen. Eller snarare: vad som ska vara externt åtkomligt från battery.c.
Alla filer som vill använda funktioner från battery.c måste inkludera battery.h.
Interna funktioner i battery.c deklareras som static i battery.c. De ska ingen annan c-fil se!
När du inkluderar .h-filen, t.ex. battery.h i t.ex. main.c så kommer all text från .h-filen att klistras in där du inkluderade den, väldigt enkelt egentligen.
När kompilatorn kompilerar din main.c och upptäcker att din main.c försöker anropa en funktion som inte finns implementerad i main.c men den är deklarerad längst upp i main.c (tack vara att allt innehåll från battery.h har klistrats in i main.c där #include var!), så köper kompilatorn det. Den "tänker": Ok här anropar du funktionen "int measurebattery(void)" den har du (genom include av battery.h) sagt att den funktionen finns, så jag köper det för tillfället, även om jag inte ser funktionen deklarerad här i main.c. Kompilatorn skapar nu en main.o (objekt-fil, ett mellansteg mellan källkod och körbar fil/laddfil).
gcc -c main.c
Sedan kan du kompilera battery.c till battery.o
gcc -c battery.c
Slutligen länkar du filerna med t.ex. gcc battery.o main.o -o mittfinaprogram
Skulle det nu vara så att du LJÖG för kompilatorn i battery.h, att funktionen "int measurebattery(void)" faktiskt inte alls finns i battery.c (och därmed inte heller i battery.o (eller nån annan .o-fil du har med)) så blir kompilatorn/länkaren arg på dig i detta steg och säger bara att hörredu! i main.o vill jag anropa "int measurebattery(void)" men den finns ingenstans att finna!
I små projekt kan man dock hoppa över objektfils-steget om man vill och köra allt i ett svep med:
gcc main.c battery.c -o mittfinaprogram
Därför kan det vara bra att veta om detta med objektfiler och att det givetvis görs så ändå även i ovanstående fall.
Hoppas jag bidrog och inte förvirrade mer.
MVH: Mikael