Header-Datei, Funktionen + globale Variablen



  • Hallo,
    ich bin nun schon eine Weile am Programmieren, habe mich aber bisher nur rudimentär mit Header-Dateien beschäftigt - zum Auslagern einiger Funktionen hat es immer genügt.

    Nun stehen sich aktuell Header-Datei und Compiler ein wenig im Weg und ich dachte mir, dass mir hier jemand ein paar Tipps geben könnte.

    Anfang bei Null:
    Ich binde Header-Dateien in der Haupt-C-Datei per

    #include "header1.h"
    #include "functions.h"
    

    ein. Soweit ich das weiß, bedeutet dies, dass der Präprozessor an genau dieser Stelle den Inhalt der Header-Dateien einfügt.
    In einer dieser Dateien steht nun z.B.:

    #ifndef FUNCTIONS_H_
    #define FUNCTIONS_H_
    void func_prototyp(void);
    #endif
    

    Hier gleich eine Frage: WIE sollte es denn zu einer doppelten Einbindung der Header-Datei kommen? Ich werde die Zeile wohl kaum zweimal in meiner Haupt-C-Datei einfügen.

    Nächster Punkt:
    Zur Header-Datei gehört eine C-datei (zumindest wenn ich den Code nicht in den Header stopfen will). Meine Beispielfunktion von oben benötigt nun z.B. folgende Includes:

    #include <stdio.h>
    #include <malloc.h>
    

    Muss ich an diesem Punkt etwas beachten, wenn ebendiese Includes bereits in der Haupt-C-Datei vorhanden sind?

    Dritte Frage:
    Ich binde 25 Funktionen per Header-Datei ein, die sich gegenseitig aufrufen. Gibt es keinen eleganten Weg, der dafür sorgt, dass ich die Reihenfolge der Funktionen in meiner header1.c-Datei unbeachtet lassen kann? Immerhin stehen die Prototypen schon im zugehörigen Header, davon weiß die .c-Datei aber scheinbar nichts.

    Viertes Problem:
    Ich benutze in meiner main.c einige globale Variablen (ein paar Arrays, Structs,...), die natürlich nicht nur die int main, sondern auch die Funktionen benötigen.
    Wo muss ich diese Variablen positionieren, damit es sie exakt EINmal gibt und jede Funktion darauf zugreifen kann?

    Meine aktuellen Schwierigkeiten liegen vor allem da, dass stets einer Header-Datei irgendetwas fehlt oder gar mehrere Sätze globaler Variablen vorhanden sind, was den Sinn selbiger irgendwie zunichte macht.

    Falls mir jemand ein paar Erklärungen geben oder mir sagen kann, wo ich möglichst detaillierte Infos hierzu finde, würde ich mich freuen :).



  • 1. Wenn dein Programm etwas länger wird und du viel modularisieren musst brauchst du das. Zum Beispiel sowas wie eine C+Headerdatei für doppelt verkettete Listen die von C+Headerdatei für einen Buffertyp benutzt wird und eine main die sowohl die Listen als auch die Buffer benutzt.

    2. Nein, da die Standardheader alle include Guards haben und du sie beliebig oft einbinden kannst. Es ist etwas umstritten, aber irgendwie schon schlechte Praxis Headerdateien in Headerdateien einzubinden. Das hat dann den Effekt dass man eine Variable hat die man nicht kennt und die nicht definiert wurde, dann sucht man in den Includes und in den Includes der Includes und so weiter.
    Wenn es geht versuche nur in den C-Dateien Includes zu benutzen. Geht aber nicht immer.

    3. Es gibt keine wirkliche Zugehörigkeit von Header und C-Datei. Wenn du eine prog.c und eine prog.h hast haben die nach Compilerlogik nichts miteinander zu tun, also musst du explizit die prog.h in prog.c einbinden und damit tut es das was du willst.

    4. Üblicherweise löst man das indem man die Variable in der C-Datei definiert und in der h-Datei deklariert.
    Sowas wie

    //main.c
    int i;
    //main.h
    extern int i;
    //sec.c
    #include main.h
    

    Einige meinen auch, dass globale Variablen böse sind. Kann ich nicht bestätigen. Dann baut man ein int geti() und ein void seti(int i), wobei geti und seti in der C-Datei definiert und im Header deklariert werden und i eine lokale statische Variable ist. Dann hat man eine quasi-globale Variable ohne dass man theoretische Probleme mit Namespaces kriegt.



  • Ok, erstmal vielen Dank für die Infos, das hat schon ziemlich geholfen :).

    Ich muss jetzt also meine Header-Datei in die (namentlich) zugehörige .c-Datei einbinden und dann kann ich (ohne irgendwas doppelt zu haben und somit mein Programm aufzublähen) meine Funktionen in beliebiger Reihenfolge eintragen. Richtig?

    Die Includes musste ich bisher auch ausschließlich in den .c-Dateien einfügen, die Frage bezog sich ja nur darauf, ob es Probleme bereitet, wenn ich sie in mehrere .c-Dateien einfügen muss.
    Soweit ich dich verstanden habe, geht das aber in Ordnung.

    Was deinen Tipp zu den Variablen angeht: Ich soll jetzt also eine Header-Datei für die .c-Datei erstellen, in der die main-Funktion zu finden ist? Klingt ganz vernünftig, wobei ich mit dem "extern" noch nichts zu tun hatte.

    Dass globale Variablen irgendwie zu vermeiden sind, ist mir auch schonmal untergekommen. Ich nutze sie auch nur sehr selten, allerdings müsste ich ohne ebendiese nahezu allen meinen Funktionen zusätzliche, identische Parameter übergeben oder mit deinem Vorschlag auf weniger globale Variablen ausweichen - für mich in diesem Fall definitiv zuviel Aufwand, nur um ein paar Formalismen nachzukommen.

    Noch eine neue Frage:
    Ich habe eine Struktur erstellt, die ich wegen ihrer Länge in eine eigene Header-Datei gepackt habe. Zusammen mit der Struktur werden gleich die später zu verwendenden Elemente erzeugt.
    Dieser "Struktur"-Header wird nun in meine main.c eingebunden (einfach per #include) - wie binde ich sie korrekt in die weiteren .c-Dateien ein? Muss ich jetzt wie bei den globalen Variablen vorgehen? Ich möchte schließlich, dass die Struktur-Elemente für alle .c-Dateien die gleichen sind, damit stets am gleichen Datensatz gearbeitet wird.



  • nwp2 schrieb:

    Es ist etwas umstritten, aber irgendwie schon schlechte Praxis Headerdateien in Headerdateien einzubinden.

    Ach was, das ist doch manchmal bei Typendeklarationen gar nicht vermeidbar, das ist gängige Praxis.



  • Stiefel2000 schrieb:

    Noch eine neue Frage:
    Ich habe eine Struktur erstellt, die ich wegen ihrer Länge in eine eigene Header-Datei gepackt habe. Zusammen mit der Struktur werden gleich die später zu verwendenden Elemente erzeugt.
    Dieser "Struktur"-Header wird nun in meine main.c eingebunden (einfach per #include) - wie binde ich sie korrekt in die weiteren .c-Dateien ein? Muss ich jetzt wie bei den globalen Variablen vorgehen? Ich möchte schließlich, dass die Struktur-Elemente für alle .c-Dateien die gleichen sind, damit stets am gleichen Datensatz gearbeitet wird.

    Du packst in den Header lediglich die Deklaration rein, also sowas z.B:

    struct my_struct
    {
    // ...
    };

    Überall dort, wo der Typ bzw. die Struktur bekannt sein muss, bindest
    du deinen Header mit include ein. Das kann natürlich auch mal eine
    andere Headerdatei sein die z.B. Funktionsprototypen enthält, die
    Strukturparameter entgegen nehmen, etc.
    Das Erzeugen der Strukturen passiert in den *.c Dateien - vorzugsweise
    in deinem Modul, das für die Erzeugung der Strukturen zuständig ist.

    struct my_struct ms[N]={0};
    struct my_struct* pms = malloc ( ...



  • Sorry, entweder durchblicke ich das nicht ganz, oder es hilft mir nicht.

    Ich benötige ein Array von Struktur-Elementen, da ich mit einer for-Schleife an jedem Element arbeiten will. Wenn ich das so dynamisch machen wollte, wie du es vorschlägst, müsste ich vermutlich ein realloc anwenden, aber ich bin nicht sicher, ob das bei der Größe, die eine Struktur haben kann, noch empfehlenswert ist. Außerdem ist im Voraus einigermaßen sicher, wieviele Elemente ich brauche.

    Wenn ich den Header mit der Struktur irgendwo einbinde, muss ich dann #ifndef etc. verwenden, oder soll ich ihn blind eintragen?

    Mal folgendes Beispiel:

    struct Struktur
    { ...
    }Struktur_Elemente[500];
    

    So steht's im Header (und aus obigen Gründen würde ich es auch gern so lassen, wenn möglich). Werden jetzt bei jedem Einbinden des Headers 500 neue Elemente erzeugt, oder handelt es sich stets um die selben, sodass hier gar kein Problem vorliegt?



  • Stiefel2000 schrieb:

    Mal folgendes Beispiel:

    struct Struktur
    { ...
    }Struktur_Elemente[500];
    

    So sollte man es nicht machen, es kann Probleme aufgrund Mehrfacheinbindung geben. Die Definition (das ist die Erzeugung der Struktur) muss aus dem Header raus, die Deklaration kommt rein:

    /////////////////////////////////////////////////////////////////
    
    // my_struct.h
    
    #ifndef MY_STRUCT_H
    #define MY_STRUCT_H
    
    struct my_struct 
    { 
    // ... 
    }; 
    extern my_struct ms[500]; // Deklaration.
    
    #endif //  MY_STRUCT_H
    
    /////////////////////////////////////////////////////////////////
    
    // my_struct.c
    
    #include "my_struct.h"
    my_struct ms[500] = {0}; // Definition und Initialisierung.
    


  • Big Brother schrieb:

    my_struct ms[500] = {0}; // Definition und Initialisierung.

    du meinst wohl: struct my_struct ms[500]=..., das ist ja kein typedef.
    🙂



  • ;fricky schrieb:

    Big Brother schrieb:

    my_struct ms[500] = {0}; // Definition und Initialisierung.

    du meinst wohl: struct my_struct ms[500]=..., das ist ja kein typedef.
    🙂

    Stimmt, wo hat sich das doofe Wort bloß versteckt?



  • Habe das so versucht, bekomme nun beim Kompilieren folgende Fehlermeldungen:

    //my_struct.h
    struct my_struct 
    { 
    // ... 
    }; 
    extern my_struct ms[500];
    
    //my_struct.c
    #include "my_struct.h"
    struct my_struct ms[500];
    
    ..\my_struct.h:85: error: syntax error before "ms"
    ..\my_struct.h:85: warning: type defaults to `int' in declaration of `ms'
    ..\my_struct.h:85: warning: data definition has no type or storage class
    

    Was soll eigentlich das "= {0};" bringen?

    Noch eine Frage:
    Kann ich ein

    #define ZAHL 7268
    

    irgendwo so unterbringen, dass ich es nicht in jeder .c-Datei unterbringen muss? Damit verlöre die Zeile nämlich ihren Sinn.



  • Stiefel2000 schrieb:

    ..\my_struct.h:85: error: syntax error before "ms"
    ..\my_struct.h:85: warning: type defaults to `int' in declaration of `ms'
    ..\my_struct.h:85: warning: data definition has no type or storage class
    

    da muss auch noch ein 'struct' hin.

    Stiefel2000 schrieb:

    Was soll eigentlich das "= {0};" bringen?

    in diesem fall ... nichts.
    🙂



  • ;fricky schrieb:

    Stiefel2000 schrieb:

    Was soll eigentlich das "= {0};" bringen?

    in diesem fall ... nichts.
    🙂

    Das initialisiert alle Elemente mit 0. In diesem Fall würde es der Compiler automatisch für dich tun, so wie er es für alle globale Variable tut, was ich gern vergesse 😃



  • Ok, dann so:

    //my_struct.h
    struct my_struct 
    { 
    // ... 
    }; 
    extern struct my_struct ms[500];
    
    //my_struct.c
    #include "my_struct.h"
    struct my_struct ms[500];
    

    Diesmal folgende Fehlermeldung:

    my_struct.h:86: error: variable-size type declared outside of any function
    

    Auch meine weitere Frage hänge ich nochmal an:
    Kann ich ein

    #define ZAHL 7268
    

    irgendwo so unterbringen, dass ich es nicht in jeder .c-Datei unterbringen muss? Eine Variable geht hier (glaub' ich) nicht, da ich damit ein statisches Array erstellen will.



  • Stiefel2000 schrieb:

    Diesmal folgende Fehlermeldung:

    my_struct.h:86: error: variable-size type declared outside of any function
    

    Zeig doch die Datei, oder kommentiere alles aus und füge schrittweise ein, bis du den Fehler eingegrenzt hast.

    Stiefel2000 schrieb:

    Auch meine weitere Frage hänge ich nochmal an:
    Kann ich ein

    #define ZAHL 7268
    

    irgendwo so unterbringen, dass ich es nicht in jeder .c-Datei unterbringen muss? Eine Variable geht hier (glaub' ich) nicht, da ich damit ein statisches Array erstellen will.

    Das define kannst du auch in einen Header packen. Ein statisches Array bekommst du auch mit einem enum hin:

    enum {my_size = 100};
    char array[my_size];
    


  • Also die angegebene Zeile 86 ist genau die, die

    extern struct my_struct ms[500];
    

    enthält.
    Ich verstehe ja durchaus die Fehlermeldung, nur leider kann ich mir keinen Reim darauf machen 😕.

    EDIT: Habe den Fehler jetzt beseitigen können (in der my_struct.c stand noch eine Variable in den eckigen Klammern), jetzt kompiliert das Programm wieder. Mal schauen, ob es noch so läuft, wie geplant.
    Erstmal vielen Dank für eure Hilfe, vielleicht kann ich ja ab jetzt Header-Dateien sinnvoller einsetzen :).


Anmelden zum Antworten