Header Dateien über eine Header einbinden

Gast #4974755
Lesenswert?

Ich habe hier ein kleines "Problem" - ich habe verschiedene Dateien 
welche auf die selben Header zugreifen. Um mir das ganze ein bisschen zu 
vereinfachen wollte ich nun dies alles einer einzelnen Header 
unterbringen, quasi so:


Die Hauptheader welche die ganzen Headerdateien includiert:

main.h
1
#ifndef MAIN_H_
2
  #define MAIN_H_
3

4
  #include "Funktionen.h"
5
  //...
6

7

8
#endif

- Funktionen.h
1
#ifndef FUNKTIONEN_H_
2
  #define FUNKTIONEN_H_
3

4
  void Funktion(void);
5

6
#endif


Und wenn ich nun in meinen anderen Dateien die Datei main.h einbinde, 
schimpft mich der Compiler / Linker an das die Funktion "Funktion" 
mehrfach deklariert ist. Kann ich das irgendwie umgehen?

- Programm1.c / Programm2.c / Sonstwas.c
1
#include "main.h"

Es ist GCC
Gast #4974813
Lesenswert?

DraconiX schrieb:
> Ich habe hier ein kleines "Problem" - ich habe verschiedene Dateien
> welche auf die selben Header zugreifen. Um mir das ganze ein bisschen zu
> vereinfachen wollte ich nun dies alles einer einzelnen Header
> unterbringen, quasi so:

Solche "Generalideen" sind im allgemeinen nicht zweckmäßig. Aber 
zugegeben: Die hatten wir alle mal. :-)


> Und wenn ich nun in meinen anderen Dateien die Datei main.h einbinde,
> schimpft mich der Compiler / Linker an das die Funktion "Funktion"
> mehrfach deklariert ist. Kann ich das irgendwie umgehen?

Die Idee des "Umgehens" ist im allgemeinen auch nicht zweckmäßig. Aber 
auch die hatten wir alle mal.

Wie lautet die Fehlermeldung genau? Von wem kommt sie genau? Vom Linker 
oder vom Compiler?

Am besten erzeugst Du ein minimales Projekt, dass für uns compilierbar 
ist und den Fehler aufweist. Bitte konkrete Angaben zu 
Entwicklungsumgebung machen.
Gast #4974820
Lesenswert?

DraconiX schrieb:
> Und wenn ich nun in meinen anderen Dateien die Datei main.h einbinde,
> schimpft mich der Compiler / Linker an das die Funktion "Funktion"
> mehrfach deklariert ist. Kann ich das irgendwie umgehen?

Ja. Und zwar, indem du das Ganze nicht derart falsch angehst wie du es 
gepostet hast, sondern es richtig machst.

Also:
Eine Header-Datei hat den Zweck, daß andere Dateien Dinge sehen und 
verwenden können, die in der eigentlichen Quell-Datei vorhanden sind. 
Also gehört keine einzige Funktion und keine einzige Variable in eine 
Headerdatei! Was da hineingehört, ist eine Referenz durch das Verwenden 
von "extern". Lediglich Typdefinitionen gehören dann (und nur dann!) in 
die Headerdatei, wenn sie von außen tatsächlich benötigt werden. ALLES 
ANDERE GEHÖRT NICHT HINEIN.

Beispiel:

in karlheinz.c steht

long Emil;
static int Meingeheimnis; // wird anderen nicht zur Kenntnis gegeben
void HauDenLukas(int Otto)
{ Emil = Otto;
}


und in der zugehörigen Headerdatei karlheinz.h steht

#ifndef KARLHEINZINCLUDED
#define KARLHEINZINCLUDED

extern long Emil;
extern void HauDenLukas(int Otto);

#endif

So, das war's. Dieses karlheinz.h kann man überall inkludieren, auch 
mehrfach, das stört nicht. Alle Programme, die dies inkludieren, kennen 
damit auch Emil und HauDenLukas und können selbige benutzen, obwohl 
beides tatsächlich in karlheinz.c steht. Hingegen Meingeheimnis steht 
nicht im Header und kann deshalb auch nicht woanders eingesehen werden. 
Das erreicht man durch das falsch verstehbare "static" (wer kommt schon 
von allein drauf, daß das sowas wie "private" bedeuten soll...)

Ist dir das jetzt klar geworden?

W.S.
#4975089
Lesenswert?

W.S. schrieb:
> Also gehört keine einzige Funktion und keine einzige Variable in eine
> Headerdatei! Was da hineingehört, ist eine Referenz durch das Verwenden
> von "extern".

Das extern ist bei Funktionen nicht nötig und auch nicht üblich.
Das vom TE gepostete Beispiel ist völlig korrekt und stellt auch nur 
eine "Referenz" (korrekter: Deklaration) dar:

DraconiX schrieb:
> - Funktionen.h
> #ifndef FUNKTIONEN_H_
>   #define FUNKTIONEN_H_
>
>   void Funktion(void);
>
> #endif
Gast #4975110
Lesenswert?

Rolf M. schrieb:
> Das extern ist bei Funktionen nicht nötig und auch nicht üblich.

Was ist denn das für ein Argument in einem formalen Konstrukt wie einer 
Programmiersprache, wenn man denn das Kultobjekt "C" mit all ihren 
Konventionen und obstrusen Freiheiten als solche bezeichnen möchte.

Da war Niklaus Wirth schon deutlich weiter
Gast #4981491
Lesenswert?

Rufus Τ. F. schrieb:
> Das Schlüsselwort extern vor Funktionsprototypen?
>
> Ach.

Ja, gut so. Nix "ach".

mag ja sein, daß du seit Jahrzehnten schludrig programmiert hast, weil 
das in C eben geht. Aber das dedizierte extern auch vor 
Funktionsprototypen hat seinen Sinn, es schafft nämlich Klarheit. Auch 
für dich, wenn du mal auf ne Quelle schaust, die du nicht selber 
geschrieben hast. Aber ich werfe dir das nicht vor, schließlich ist es 
für C-Programmierer ja ein richtiger Seelenschmerz, irgend etwas 
ausführlich und dediziert hinzuschreiben. Am liebsten würden sie die 
Welt in ein einziges nonprintable-Zeichen zusammenfassen.

W.S.
#4981932
Lesenswert?

W.S. schrieb:
> mag ja sein, daß du seit Jahrzehnten schludrig programmiert hast, weil
> das in C eben geht.

Daran ist rein gar nichts "schludrig".

> Aber das dedizierte extern auch vor Funktionsprototypen hat seinen Sinn,
> es schafft nämlich Klarheit.

Ich hab bisher außer bei dir noch nie gesehen, dass jemand das so macht. 
Und ich habe noch nie erlebt, dass deshalb irgendwem etwas unklar ist. 
Wenn überhaupt, würden sich bei deiner Schreibweise einige wundern und 
sich fragen, was das bedeutet, weil sie es vorher noch nie gesehen 
haben.
Du schreibst dann wohl auch bei jeder nicht-statischen lokalen Variable 
auch das dafür vorgesehene "auto" davor und statt "short s;" lieber 
"short int s;", weil es "schludrig" wäre, das wegzulassen?
Gast #4982008
Lesenswert?

Also das eine Funktion Extern definiert wird, ist mir auch schon des 
öfteren über den Weg gelaufen, selbst von CubeMX erzeugter Code 
verwendet dies. Der Sinn dahinter ist mir eigentlich auch relativ 
schnell klar geworden wenn man die Datei und Verlinkungs-Hierarchie 
betrachtet.
#4982364
Lesenswert?

Genauso wie fast alle Compiler "signed" als implizit annehmen wenn man's 
nicht über "unsigned" explizit anders einstellt.

Bis, ja bis irgendwann irgendein Compilerhersteller auf die glorreiche 
Idee kommt und meint, dass der Standard ja schon viel zu lange falsch 
ist und es eigentlich andersrum gehört. Ja, MS, ich schau in Deine 
Richtung! Bisher hat noch JEDE Visual Studio Version irgendetwas an das 
man sich endlich gewöhnt hatte wieder über den Haufen werfen müssen.

Und dann kompilieren "sonderbarerweise" viele Programme nicht mehr oder 
stürzen mit sehr seltsamen Fehlern in Grenzbereichen ab.

Schon deswegen ist es sicher kein Fehler, das was "eh implizit" vom 
Compiler angenommen wird auch explizit so hinzuschreiben. Wer weiß was 
die nächste Version des Compilers für "eh implizit" ansieht.
#4982476
Lesenswert?

Mit so einem Super-H-File wird bedingtes Compilieren fast unmöglich. Bei 
einer Änderung in einem beliebigen .h File muss immer das gesamte 
Projekt neu übersetzt werden. Sehr praktisch am Anfang, aber mit 
zunehmender Projektgröße immer nerviger. Dummerweise ist es dann fast 
unmöglich, das Konstrukt wieder loszuwerden.

Gruß, Stefan
Gast #4985851
Lesenswert?

Rolf M. schrieb:
> Du schreibst dann wohl auch bei jeder nicht-statischen lokalen Variable
> auch...

Ich halte solche Einwendungen bzw. Unterstellungen für dumme Sprüche - 
um das mal klar zu sagen.

Das ist die gleiche Ebene wie die müßige Diskussion über Vorrang-Regeln. 
Auch dort ist es allemal einfach besser, ne ordentliche Klammer zu 
setzen.

Das schafft ne finale Klarheit, eben genau wie das 'extern' vor externen 
Funktionen.

Sollte doch einleuchtend sein!

Gute Lesbarkeit und damit auch gute Wartbarkeit usw. erreicht man nicht 
durch das Hinweisen darauf, daß man ja dies und das auch weglassen kann, 
weil der Compiler in solchen Fällen eine Default-Regelung hernimmt.

Ich sehe C eher mit gewissem Abstand. Da fallen einem die Defizite von C 
auf, für die ein reiner C-Programmierer schon längst betriebsblind 
geworden ist. Vielleicht ist es das, was dir so unbekannt vorkommt und 
deswegen deine Verwunderung auslöst.

W.S.
Gast #4985886
Lesenswert?

W.S. schrieb:
> Das schafft ne finale Klarheit, eben genau wie das 'extern' vor externen
> Funktionen.

Das heißt du packst das extern an alle Funktions Deklarationen in 
Headern? Da das aber an allen Funktionen dran steht ist es etwas 
witzlos, weil es nichts unterscheidet - kein Informationsgewinn. Man 
kann sich das extern ja hin denken, wenn es eine .h Datei ist.
Außerdem ist die Funktion ja nicht mehr extern wenn du den Header von 
der .c Datei aus inkludierst, welche die Funktion auch definiert. Machst 
du dann eine Konstruktion mit #ifdef um das extern dann abzuschalten? Ob 
das besser lesbar ist?
Gast #4988077
Lesenswert?

Dr. Sommer schrieb:
> Das heißt..

.. daß es eben ein Unterschied ist, ob eine Vorwärts-Definition in der 
gleichen Quelle aufgelöst wird oder in einer anderen Quelle.

Das heißt aber auch, daß das System der Modularisierung bei C einfach 
nur stümperhaft und schlecht gelöst ist. Pascal macht das um Längen 
besser. Aber auch diese Diskussion ist müßig. Wenn's dich wirklich 
interessieren sollte, dan guck einfach in das, was ich gelegentlich 
poste. Dort siehst du, wonach du fragst.

So. Die hier aufgekommene Diskussion über 'extern" kann man schlichtweg 
in die Frage zusammenfassen:

Soll man Default-Annahmen in C ausnutzen beim Programmieren oder nicht?

Hier kann nun ein jeder sein Kreuzchen bei

  [   ]ja   oder  [   ]nein

machen.

Meine Antwort dazu ist ein klares nein. Man sollte Default-Regelungen 
als eine Art Sicherheits-Schicht auffassen, also etwa wie ein Netz unter 
dem Trapez, auf dem man grad herumturnt. Folglich soll man nur mit 
Vorsicht sich drauf stützen und beim Programmieren mit Augenmaß 
arbeiten, denn es geht ja nicht nur um das, was der Compiler darunter 
versteht, sondern auch darum, was ein menschlicher Leser da sieht und 
versteht. Also sollte man Variablen vor dem ersten Lesen auch mal 
beschrieben haben, bei Ausdrücken mit Klammern arbeiten wo es Sinn 
macht, bei externen Variablen, echten Konstanten und Funktionen nicht 
auf das 'extern' verzichten, sich nicht drauf verlassen, daß ab 
Programmstart aller RAM tatsächlich abgelöscht ist, und und und.. und so 
weiter.

Es sei denn, man will das Verstehen seines eigenen Geschreibsels 
behindern. Soll ja genug Leute geben, denen Obfuskation am Herzen liegt.

So, das war's.

W.S.
#4989131
Lesenswert?

W.S. schrieb:
> So. Die hier aufgekommene Diskussion über 'extern" kann man schlichtweg
> in die Frage zusammenfassen:
>
> Soll man Default-Annahmen in C ausnutzen beim Programmieren oder nicht?
>
> Hier kann nun ein jeder sein Kreuzchen bei
>
>   [   ]ja   oder  [   ]nein
>
> machen.
> Meine Antwort dazu ist ein klares nein.

Siehst du, und genau deshalb habe ich gefragt, ob du dann bei lokalen 
Variablen "auto" und bei "short int" das "int" auch hinschreibst, was du 
dann als "Unterstellung" und "dumme Sprüche" bezeichnet hast. Wenn du 
das weglässt, sind das aber auch "Default-Annahmen", ganz genau wie bei 
"extern". Beantwortet hast du die Frage, ob du auch da konsequent deiner 
eigenen Regel folgst, aber nicht.

Für mich geht es an der Stelle wie oben schon geschrieben auch nicht um 
die von dir genannte Frage, sondern um diese:

Soll man

a) Sich lieber an den allgemein üblichen Stil der Programmiersprache 
halten, in der man programmiert oder

b) lieber sein eigenes Ding machen?

Und da ist meine Antwort a).

> Folglich soll man nur mit Vorsicht sich drauf stützen und beim
> Programmieren mit Augenmaß  arbeiten, denn es geht ja nicht nur um das,
> was der Compiler darunter versteht, sondern auch darum, was ein
> menschlicher Leser da sieht und versteht.

Dazu muss man nicht alles, was man theoretisch hinschreiben könnte, auch 
zwingend hinschreiben. "Augenmaß" ist da genau das richtige Stichwort: 
"auto" schreibe ich nicht vor jede nicht-statische lokale Variable, weil 
es keinen Informationsgewinn bietet und praktisch nur unnötiges Rauschen 
ist - wie "extern" bei Funktionsdeklarationen.

> Es sei denn, man will das Verstehen seines eigenen Geschreibsels
> behindern.

Gut, sofern andere deinen Code nicht lesen müssen, sondern nur du 
selbst, kannst du natürlich auf die allgemeinen Gepflogenheiten 
verzichten und einen Stil nutzen, der speziell auf dich selbst 
abgestimmt ist.
#4989153
Lesenswert?

Das Beispiel mit auto ist ganz übel, weil es ein Relikt aus "B" ist ... 
Weg damit. In block-scope ist das default.

Außerdem: falls man den Blick auf C++ erweitern möge: dort ist das 
abgeschafft und an anderer(!) Stelle als Typinferenz wieder eingeführt 
worden.

Aber das hilft ja dem TO nicht.

Vielleicht sollte der seine old-school include-guards auf

#pragma once

umstellen (hat ja einiger Vorteile).

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren