Forum: Compiler & IDEs Frage zu Header-Files


von Dennis H. (t1w2i3s4t5e6r)


Lesenswert?

Hallo!

Ich würde mich noch als C-Anfänger bezeichnen. Habe einiges in Assembler 
gemacht und schwenke jetzt auf C um. Ich habe mir bereits den Artikel zu 
Include-Files durchgelesen und der verlinkte Thread in diesem Artikel 
bietet viel Information. Trotzdem ist mir noch eine Frage offen 
geblieben.
Nehmen wir ein ganz einfaches Beispiel. Eine Tasterentprellung. Ich habe 
also eine Timer.c, eine Main.c und eine Funktion.c
In der Timer.c ist mein Code vom Timer-Interrupt, also für die 
Entprellung, welche, wenn ein Tastendruck erkannt wurde, eine Variable 
"flag" auf 1 setzt. Diese Variable wird im Hauptprogramm in der Main.c 
abgefragt und wenn "flag" true ist, wird die Funktion aus der Funktion.c 
ausgeführt. Die Variable flag ist also global und wegen Interrupt ist 
sie volatile. Ich würde diese Variable in der Timer.c definieren. Jetzt 
muss aber in der Main.c diese Variable ja auch bekannt 
gemacht/deklariert werden. Also mit extern flag. Meine Frage ist jetzt, 
schreibe ich das extern flag in die Main.h oder in die Timer.h und 
includiere die Timer.h dann in der Main.h?
Und wie verhält es sich mit der Funktion aus Funktion.c? Setze ich einen 
Prototypen in meine Funktion.h und includiere diese dann in der Main.h 
oder schreibe ich den Prototypen in die Main.h?

Sicherlich für manche eine etwas lächerliche Frage, aber ich als 
Anfänger kann gerade einfach nicht abschätzen, was bei größeren 
Projekten wohl übersichtlicher wäre. Ich danke euch schonmal für eure 
Antworten.


MfG Dennis

von Ingo (Gast)


Lesenswert?

Dennis H. schrieb:
> Meine Frage ist jetzt,
> schreibe ich das extern flag in die Main.h oder in die Timer.h und
> includiere die Timer.h dann in der Main.h?

Kann man machen wie man will, ich schreibe globale Variablen immer in 
die main.h

Dennis H. schrieb:
> Und wie verhält es sich mit der Funktion aus Funktion.c? Setze ich einen
> Prototypen in meine Funktion.h und includiere diese dann in der Main.h
> oder schreibe ich den Prototypen in die Main.h?
Funktionsprototypen würde ich immer in die dazugehörige *.h packen. 
Headerfiles includest du immer nur in *.c Files, nicht in header, das 
führt schnell zu Chaos.

Man muss sich eine Strategie überlegen. Entweder in alle Sourcefiles 
nur die main.h , dann includest du alle anderen Header in die main.h, 
oder du includest in die Sourcefiles nur die Header die du auch 
benötigst. Ist glaube ich geschmackssache...


Ingo

von Dennis H. (t1w2i3s4t5e6r)


Lesenswert?

Hallo!

Ingo schrieb:
> Kann man machen wie man will, ich schreibe globale Variablen immer in
> die main.h

Das macht Sinn, hätte ich auch selbst drauf kommen können.

Ingo schrieb:
> Funktionsprototypen würde ich immer in die dazugehörige *.h packen.
> Headerfiles includest du immer nur in *.c Files, nicht in header, das
> führt schnell zu Chaos.

Mh, aber genau das wird im angesprochenen Artikel über Include-Files 
gemacht. Weil gerade ein Main.c kann es ja dann passieren, das man 
Massen von *.h Dateien includieren muss und der Code wieder etwas lang 
wird. Wenn das in der Main.h alles includiert wird, was die Main.c 
benötigt sollte das doch nicht verkehrt sein. Und gegen 
Mehrfachincludierung kann man sich ja absichern, wie im Artikel 
beschrieben und auch bei vielem Code schon so gesehen.

MfG Dennis

von Ingo (Gast)


Lesenswert?

Ja, aber wenn du die main.h noch woanders includest z.B. für die 
globalen Variablen? Mmmh, ich weiß nicht. Dann mach's doch so wie ich es 
noch vorgeschlagen habe, alle Header in die main.h und die main.h dann 
in alle Sourcefiles. Ich denke das man dein Artikel auch.

Aber grundsätzlich beschreibt ein Header das dazugehörige Sourcefile, 
die main.h kann da offensichtlich eine Ausnahme sein.


Ingo

von Karl H. (kbuchegg)


Lesenswert?

Dennis H. schrieb:

> Mh, aber genau das wird im angesprochenen Artikel über Include-Files
> gemacht. Weil gerade ein Main.c kann es ja dann passieren, das man
> Massen von *.h Dateien includieren muss und der Code wieder etwas lang
> wird.

Ingo hat hier auch nicht ganz recht.
Es kann auch und es ist auch sinnvoll, Header Files in Header Files zu 
inkludieren.

> Wenn das in der Main.h alles includiert wird, was die Main.c
> benötigt sollte das doch nicht verkehrt sein.

Das allerdings ist die falsche Vorgehensweise.
Um solche Dinge zu entscheiden, haltest du dich einfach an die ganze 
einfache Regel:

Wenn ich nur dieses eine File betrachte, losgelöst von allen anderen 
(egal ob h-File oder c-File): Welche Informationen von ausserhalb 
benötigt dieses File?

Das ist die Frage, die du dir stellst und die dich zur Entscheidung 
bringt welche anderen Files zu inkludieren sind.

Hast du also ein Head File, in dem eine Funktion deklariert wird, die 
einen Struktur-Pointer als Argument nimmt
1
#ifndef FILEA_H_INCLUDED
2
#define FILEA_H_INCLUDED
3
4
void AddToList( struct Entry *pEntry );
5
6
#endif

dann lautet die Frage hier: Wo kommt eigentich der struct Entry her?
Und wenn die Antwort darauf lautet: Der steht in einem File "Entry.h"
1
#ifndef ENTRY_H_INCLUDED
2
#define ENTRY_H_INCLUDED
3
4
#define MAX_ENTRY_TEXT_LEN 20
5
6
struct Entry
7
{
8
  uint8_t  timeStamp;
9
  uint16_t temperature;
10
  char     text[MAX_ENTRY_TEXT_LEN];
11
};
12
13
#endif

dann musst du (ok, in diesem Fall käme man auch mit einer 
Forward-Deklaration durch) in FileA.h dann auch Entry.h inkludieren, 
damit dort bekannt ist, dass es eine struct Entry auch tatsächlich gibt.
1
[C]
2
#ifndef FILEA_H_INCLUDED
3
#define FILEA_H_INCLUDED
4
5
#include "Entry.h"
6
7
void AddToList( struct Entry *pEntry );
8
9
#endif

Was du aber nicht haben willst, das ist, dass du dem Verwender, zb einer 
Datei Messung.c aufbürdest, zu wissen, dass es Entry.h inkludieren muss, 
wenn es FileA.h verwenden will.
Also es soll nicht so sein

Messung.c
1
#include "Entry.h"
2
#include "FileA.h"
3
....
wenn der einzige Grund für den Enry.h Include der ist, das er für 
FileA.h benötigt wird. Statt dessen soll es so sein

Messung.c
1
#include "FileA.h"
2
...
(und weil sich FileA.h den Entry.h selber zieht, geht das dann auch).

Jedes File, egal ob Header File oder Source File, zieht sich mit einem 
#include die Files selber rein, die es selbst benötigt. Gerade bei 
Header Files willst du NICHT haben, dass derjenige der das Header File 
benutzt wissen muss, welche anderen Header Files zusätzlich auch noch 
notwendig sind. Halte dich einfach an die Regel: Jedes File soll für 
sich selbst in sich abgeschlossen sein und nicht darauf angewiesen sein, 
dass der Verwenderr die Include Reihenfolge richtig hat.
Einzig für das jeweils zusammen gehörende Pärchen aus Header/Source 
macht man eine Ausnahme: Wenn FileA.h den Entry.h schon inkludiert, dann 
lässt man den #include dafür in FileA.c weg. FileA.c inkludiert sowieso 
FileA.h und kommt über diesen Umweg zum entsprechenden Include. D.h. 
hier wird eine kleine Ausnahme gemacht und Wissen aus dem Header File 
auch im Source File benutzt. Aber nur deswegen, weil dieses File-Pärchen 
ja sowieso enger 'gekoppelt' ist als zu allen anderen Files.

Dann fährst du am Besten und am Einfachsten.

von Karl H. (kbuchegg)


Lesenswert?

Für globale Variablen geht man so vor:

Zu irgend jemanden (irgendeinem Modul) gehören ja die globalen 
Variablen. Die Variablen für eine FIFO gehören logisch gesehen zur FIFO. 
Die Variablen für eine UART gehören logisch gesehen zur UART. Die 
Variablen zur Temperaturmessung gehören zu den Funktionen dazu, die die 
Temperaturmessung machen. Die LCD Variablen zum LCD-Modul etc. etc.

Das jeweilige Modul stellt diese Variablen der Allgemeinheit zur 
Verfügung. Und es tut dies, in dem es diese Variablen in den 'Pool' für 
globale Variablen stellt.

Daraus folgt: Lass die Dinge dort, wo sie hingehören.

Die global verfügbar gemachten Variablen des LCD-Moduls bleiben im 
LCD-Modul und werden dort im C File definiert. Im zugehörigen Header 
File kommt die extern-Deklaration rein. Die Variablen des UART-Moduls 
bleiben im Source-File mit den UART Funktionen definiert. Im Header File 
des UART-Moduls kommt die entsprechende extern Deklaration rein. Die 
Variablen der Stack Funktionalität (die global verfügbar gemacht werden 
sollen) bleiben im Source File mit den Stack-Funktionen ...
1
#include "stack.h"
2
3
uint8_t NrOfEntries;
4
static uint8_t Entries[STACK_MAX_NR_ENTRIES];
5
6
void push( uint8_t value )
7
{
8
  if( NrOfEntries < STACK_MAX_NR_ENTRIES )
9
    Entries[NrOfEntries++] = value;
10
}
11
12
uint8_t pop( void )
13
{
14
  if( NrEntries == 0 )
15
    return 0;
16
17
  return Entries[--NrOfEntries];
18
}
..., die entsprechenden extern Deklarationen kommen ins Header File zum 
Stack.
1
#ifndef STACK_H_INCLUDED
2
#define STACK_H_INCLUDED
3
4
#include <stdint.h>
5
6
#define STACK_MAX_NR_ENTRIES 20
7
extern uint8_t NrOfEntries;
8
9
void push( uint8_t value );
10
uint8_t pop( void );
11
12
#endif

Damit hast du erreicht:
Wenn jemand auf irgendwelche Dinge, die dem Stack betreffen zugreifen 
will, sei es Funktionen aufrufen oder auf entsprechende globale 
Variablen zugreifen will, dann inkludiert er stack.h und hat damit 
seinen Zugriff. Durch den Include von stack.h verfügt er über alles was 
er über den Stack wissen muss und was er benötigt um den Stack benutzen 
zu können.

Im Einzelfall kann man von diesem Schema auch schon mal abweichen. Aber 
im ersten Anlauf fährt man mit so einem Schema meistens sehr gut.

Was du auf keinen Fall haben willst bzw. was du auf jeden Fall vermeiden 
willst: Das Code ausserhalb von stack.h bzw. stack.c irgendwelche 
Klimmzüge abgesehen vom include machen muss, um mit dem Stack arbeiten 
zu können. Bzw. auch die Umkehrung: Wenn etwas nicht im Header File 
auftaucht, dann ist das für den Verwender (zb die Temperatur-Funktionen) 
der klare Fingerzeig: "Lass das in Ruhe - du hast an diesen Variablen 
nichts verloren!". Die Temperatur-Aufnehme-Funktionen haben sich nicht 
direkt am Array 'Entries' zu schaffen zu machen - dafür gibt es 
Funktionen, die sie zu benutzen zu haben. 'Entries' taucht daher im 
Header File gar nicht auf und wird statt dessen als File-static im 
Source File definiert. Für Code der nicht zu den Stack Funktionen 
gehört, diese aber verwenden will, steht alles was sie dazu wissen 
müssen bzw. brauchen, im Header File und nur im Header File.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Dennis H. schrieb:

> Nehmen wir ein ganz einfaches Beispiel. Eine Tasterentprellung. Ich habe
> also eine Timer.c, eine Main.c und eine Funktion.c
> In der Timer.c ist mein Code vom Timer-Interrupt, also für die
> Entprellung, welche, wenn ein Tastendruck erkannt wurde, eine Variable
> "flag" auf 1 setzt. Diese Variable wird im Hauptprogramm in der Main.c
> abgefragt und wenn "flag" true ist, wird die Funktion aus der Funktion.c
> ausgeführt. Die Variable flag ist also global und wegen Interrupt ist
> sie volatile. Ich würde diese Variable in der Timer.c definieren. Jetzt
> muss aber in der Main.c diese Variable ja auch bekannt
> gemacht/deklariert werden. Also mit extern flag. Meine Frage ist jetzt,
> schreibe ich das extern flag in die Main.h oder in die Timer.h und
> includiere die Timer.h dann in der Main.h?

In Timer.c:
 
1
#include "Timer.h"
2
3
volatile uint8_t flag;
 
wobei "flag" nicht wirklich ein guter Name ist.

 
 
in Timer.h:
1
#include <stdint.h> // wegen uint8_t
2
3
extern volatile uint8_t flag;
 
In Main.h:
 
1
#include "Timer.h"

> Und wie verhält es sich mit der Funktion aus Funktion.c? Setze ich einen
> Prototypen in meine Funktion.h und includiere diese dann in der Main.h

Ja.  Das geht ganz analog.

> oder schreibe ich den Prototypen in die Main.h?

Nein.

von Dennis H. (t1w2i3s4t5e6r)


Lesenswert?

Hallo!

@Karl Heinz

Danke für deine ausführliche Erklärung, auch wenn ich deinen ersten Post 
noch ein paar mal lesen muss, um ihn ganz zu verstehen, aber ich denke, 
ich bekomme das hin. Das mit den globalen Variablen erscheint logisch, 
das ich sie in dem File definiere, in dem sie gebraucht werden, so war 
auch mein Gedankengang.

Johann L. schrieb:
> wobei "flag" nicht wirklich ein guter Name ist.

Guter oder nicht guter Name lässt sich immer streiten, für mein 
simpelst-Beispiel hat er ausgereicht um das ihr wisst, was ich meine :)

Auf jedenfall vielen Dank für die guten Antworten


MfG Dennis

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.