Frage

Ich bin es gewohnt, meine gesamte Codierung in einer C-Datei zu erledigen.Ich arbeite jedoch an einem Projekt, das so groß ist, dass dies unpraktisch wird.Ich habe sie zusammen #eingefügt, bin aber auf Fälle gestoßen, in denen ich einige Dateien mehrmals #eingefügt habe usw.Ich habe von .h-Dateien gehört, bin mir aber nicht sicher, welche Funktion sie haben (oder warum es besser ist, zwei Dateien zu haben als eine).

Welche Strategien sollte ich zum Organisieren meines Codes verwenden?Ist es möglich, „öffentliche“ Funktionen für eine bestimmte Datei von „privaten“ zu trennen?

Das Die Frage löste meine Anfrage aus.Die Datei tea.h verweist nicht auf die Datei tea.c.Weiß der Compiler, dass jede .h-Datei eine entsprechende .c-Datei hat?

War es hilfreich?

Lösung

Sie sollten .h-Dateien als betrachten Schnittstellendateien Ihrer .c-Datei.Jede .c-Datei stellt ein Modul mit einer bestimmten Funktionalität dar.Wenn Funktionen in einer .c-Datei von anderen Modulen verwendet werden (z. B.andere .c-Dateien) fügen Sie den Funktionsprototyp in die .h-Schnittstellendatei ein.Indem Sie die Schnittstellendatei in die .c-Datei Ihres ursprünglichen Moduls und in jede andere .c-Datei, in der Sie die Funktion benötigen, einbinden, machen Sie diese Funktion anderen Modulen verfügbar.

Wenn Sie eine Funktion nur in einer bestimmten .c-Datei benötigen (nicht in einem anderen Modul), deklarieren Sie ihren Gültigkeitsbereich als statisch.Dies bedeutet, dass es nur aus der C-Datei heraus aufgerufen werden kann, in der es definiert ist.

Gleiches gilt für Variablen, die über mehrere Module hinweg verwendet werden.Sie sollten in die Header-Datei gelangen und dort mit dem Schlüsselwort „extern“ markiert werden.Notiz:Für Funktionen ist das Schlüsselwort „extern“ optional.Funktionen gelten immer als „extern“.

Die Einschlussschutzvorrichtungen in Header-Dateien helfen dabei, dieselbe Header-Datei nicht mehrmals einzubinden.

Zum Beispiel:

Modul1.c:

    #include "Module1.h"

    static void MyLocalFunction(void);
    static unsigned int MyLocalVariable;    
    unsigned int MyExternVariable;

    void MyExternFunction(void)
    {
        MyLocalVariable = 1u;       

        /* Do something */

        MyLocalFunction();
    }

    static void MyLocalFunction(void)
    {
      /* Do something */

      MyExternVariable = 2u;
    }

Modul1.h:

    #ifndef __MODULE1.H
    #define __MODULE1.H

    extern unsigned int MyExternVariable;

    void MyExternFunction(void);      

    #endif

Modul2.c

    #include "Module.1.h"

    static void MyLocalFunction(void);

    static void MyLocalFunction(void)
    {
      MyExternVariable = 1u;
      MyExternFunction();
    }

Andere Tipps

Versuchen Sie, jede .c-Datei auf einen bestimmten Funktionsbereich zu konzentrieren.Verwenden Sie die entsprechende .h-Datei, um diese Funktionen zu deklarieren.

Jede .h-Datei sollte einen „Header“-Schutz um ihren Inhalt haben.Zum Beispiel:

#ifndef ACCOUNTS_H
#define ACCOUNTS_H
....
#endif

Auf diese Weise können Sie „accounts.h“ so oft einbinden, wie Sie möchten, und das erste Mal, dass es in einer bestimmten Kompilierungseinheit angezeigt wird, ist das einzige, das tatsächlich seinen Inhalt abruft.

Compiler

Ein Beispiel für ein C-Modul finden Sie unter dieses Thema - Beachten Sie, dass es zwei Dateien gibt – den Header tea.h und den Code tea.c.Sie deklarieren im Header alle öffentlichen Definitionen, Variablen und Funktionsprototypen, auf die andere Programme zugreifen sollen.In Ihrem Hauptprojekt verwenden Sie #include und dieser Code kann nun auf die Funktionen und Variablen des Tea-Moduls zugreifen, die in der Kopfzeile erwähnt werden.

Danach wird es etwas komplexer.Wenn Sie Visual Studio und viele andere IDEs verwenden, die Ihren Build für Sie verwalten, ignorieren Sie diesen Teil – sie kümmern sich um das Kompilieren und Verknüpfen von Objekten.

Linker

Wenn Sie zwei separate C-Dateien kompilieren, erstellt der Compiler einzelne Objektdateien – main.c wird also zu main.o und tea.c wird zu tea.o.Die Aufgabe des Linkers besteht darin, sich alle Objektdateien (Ihre main.o und tea.o) anzusehen und die Referenzen abzugleichen. Wenn Sie also eine Tea-Funktion in main aufrufen, ändert der Linker diesen Aufruf so, dass er tatsächlich die richtige Funktion aufruft Funktion im Tee.Der Linker erzeugt die ausführbare Datei.

Da ist ein tolles Tutorial Hier wird näher auf dieses Thema eingegangen, einschließlich des Umfangs und anderer Probleme, auf die Sie stoßen werden.

Viel Glück!

-Adam

Ein paar einfache Regeln für den Anfang:

  1. Fügen Sie die Deklarationen, die Sie „öffentlich“ machen möchten, in die Header-Datei für die C-Implementierungsdatei ein, die Sie erstellen.
  2. Nur #include Header-Dateien in die C-Datei aufnehmen, die zum Implementieren der C-Datei erforderlich sind.
  3. Fügen Sie Header-Dateien nur dann in eine Header-Datei ein, wenn dies für die Deklarationen in dieser Header-Datei erforderlich ist.

  4. Verwenden Sie die von Andrew beschriebene Include-Guard-Methode ODER verwenden Sie #pragma einmal wenn der Compiler dies unterstützt (was das Gleiche tut – manchmal effizienter)

Um Ihre Zusatzfrage zu beantworten:

DasDie Frage löste meine Anfrage aus.Das tea.h enthält keinen Verweis auf die Datei tea.c-Datei."weiß" der Compiler dass jede .h-Datei eine entsprechende .c-Datei?

Der Compiler kümmert sich nicht primär um Header-Dateien.Bei jedem Aufruf des Compilers wird eine Quelldatei (.c) in eine Objektdatei (.o) kompiliert.Hinter den Kulissen (d. h.im make Datei oder Projektdatei) wird eine entsprechende Befehlszeile generiert:

compiler --options tea.c

Die Quelldatei #includes sind alle Header-Dateien für die Ressourcen, auf die es verweist. Auf diese Weise findet der Compiler Header-Dateien.

(Ich beschönige hier einige Details.Es gibt viel über das Erstellen von C-Projekten zu lernen.)

Zusätzlich zu den oben gegebenen Antworten besteht ein kleiner Vorteil der Aufteilung Ihres Codes in Module (separate Dateien) darin, dass Sie, wenn Sie globale Variablen benötigen, deren Umfang mithilfe des Schlüsselworts auf ein einzelnes Modul beschränken können. statisch'.(Sie können dies auch auf Funktionen anwenden).Beachten Sie, dass sich diese Verwendung von „static“ von der Verwendung innerhalb einer Funktion unterscheidet.

Ihre Frage macht deutlich, dass Sie nicht wirklich viel ernsthafte Entwicklung betrieben haben.Der übliche Fall ist, dass Ihr Code im Allgemeinen viel zu groß ist, um in eine Datei zu passen.Eine gute Regel ist, dass Sie die Funktionalität in logische Einheiten (.c-Dateien) aufteilen sollten und jede Datei nicht mehr enthalten sollte, als Sie gleichzeitig problemlos im Kopf haben können.

Ein bestimmtes Softwareprodukt umfasst dann im Allgemeinen die Ausgabe vieler verschiedener .c-Dateien.Normalerweise geschieht dies dadurch, dass der Compiler eine Reihe von Objektdateien erzeugt (in Unix-Systemen „.o“-Dateien, VC generiert .obj-Dateien).Der Zweck des „Linkers“ besteht darin, diese Objektdateien in der Ausgabe (entweder einer gemeinsam genutzten Bibliothek oder einer ausführbaren Datei) zusammenzusetzen.

Im Allgemeinen enthalten Ihre Implementierungsdateien (.c) tatsächlich ausführbaren Code, während die Header-Dateien (.h) die Deklarationen der öffentlichen Funktionen in diesen Implementierungsdateien enthalten.Sie können ganz einfach mehr Header-Dateien als Implementierungsdateien haben, und manchmal können Header-Dateien auch Inline-Code enthalten.

Es ist im Allgemeinen recht ungewöhnlich, dass Implementierungsdateien einander enthalten.Eine bewährte Vorgehensweise besteht darin, sicherzustellen, dass jede Implementierungsdatei ihre Anliegen von den anderen Dateien trennt.

Ich würde Ihnen empfehlen, den Linux-Kernel herunterzuladen und sich die Quelle anzusehen.Für ein C-Programm ist es ziemlich umfangreich, aber gut in separate Funktionsbereiche unterteilt.

Die .h-Dateien sollten zum Definieren der Prototypen für Ihre Funktionen verwendet werden.Dies ist notwendig, damit Sie die benötigten Prototypen in Ihre C-Datei aufnehmen können, ohne alle benötigten Funktionen in einer Datei deklarieren zu müssen.

Zum Beispiel, wenn Sie #include <stdio.h>, dies stellt die Prototypen für printf und andere IO-Funktionen bereit.Die Symbole für diese Funktionen werden normalerweise standardmäßig vom Compiler geladen.Sie können sich die .h-Dateien des Systems unter /usr/include ansehen, wenn Sie sich für die normalen Redewendungen dieser Dateien interessieren.

Wenn Sie nur triviale Anwendungen mit wenigen Funktionen schreiben, ist es nicht wirklich notwendig, alles in logische Gruppierungen von Prozeduren zu modularisieren.Wenn Sie jedoch ein großes System entwickeln müssen, müssen Sie einige Überlegungen anstellen, wo Sie die einzelnen Funktionen definieren.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top