Forum: PC-Programmierung Header Dateien über eine Header einbinden


von DraconiX (Gast)


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

von Dirk B. (dirkb2)


Lesenswert?

DraconiX schrieb:
> schimpft mich der Compiler / Linker an das die Funktion "Funktion"
> mehrfach deklariert ist.

Wer meckert denn nun?
Bitte die genaue Fehlermeldung (per Copy&Paste).

von Wolfgang (Gast)


Lesenswert?

DraconiX schrieb:
> #ifndef MAIN_H_
>   #define MAIN_H_
>
>   #include "Funktionen.h"

> #ifndef FUNKTIONEN_H_

Das ist doppelt gemoppelt. Wozu?

von Theor (Gast)


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.

von W.S. (Gast)


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.

von Rolf M. (rmagnus)


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

von C-Kritiker (Gast)


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

von Rolf M. (rmagnus)


Lesenswert?

C-Kritiker schrieb:
> Rolf M. schrieb:
>> Das extern ist bei Funktionen nicht nötig und auch nicht üblich.
>
> Was ist denn das für ein Argument

Du hältst es nicht für sinnvoll, sich an Konventionen zu halten?

von Heinz L. (ducttape)


Lesenswert?

Wo genau bitte ist Euer Problem? Was W.S. schreibt ist, recht 
weitschweifend formuliert aber doch, genau der Weg der seit Jahrzehnten 
für C++ Header den Standard darstellt.

Aber ohne die Diskussion um des Kaisers Bart geht's in dem Forum wohl 
nicht.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Heinz L. schrieb:
> genau der Weg der seit Jahrzehnten für C++ Header den Standard
> darstellt.

Das Schlüsselwort extern vor Funktionsprototypen?

Ach.

von W.S. (Gast)


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.

von Wegstaben V. (wegstabenverbuchsler)


Lesenswert?

W.S. schrieb:
> 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.

ich glaube, da verwechselst du was mit Assembler Programmierer

von Rolf M. (rmagnus)


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?

von DraconiX (Gast)


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.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

"extern" ist bei Funktionsprototypen implizit.

Das hier bedeutet also exakt das gleiche, nein, sogar das selbe :
1
extern int bla(int fusel);
2
3
int bla(int fusel);

: Bearbeitet durch User
von Heinz L. (ducttape)


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.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Heinz L. schrieb:
> Bisher hat noch JEDE Visual Studio Version irgendetwas an das man sich
> endlich gewöhnt hatte wieder über den Haufen werfen müssen.

Tatsächlich? Und Du beziehst Dich auf C bzw. C++? Das würde mich 
wundern, denn insbesondere bei C++ ist MS seit Jahren ausgesprochen 
standardkonform.

Was magst Du meinen?

von Stefan K. (stefan64)


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

von W.S. (Gast)


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.

von Dr. Sommer (Gast)


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?

von W.S. (Gast)


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.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Die Alternative ist es, das verwendete Werkzeug benutzen zu lernen.

von W.S. (Gast)


Lesenswert?

Rufus Τ. F. schrieb:
> Die Alternative ist...

Nein.

Das ist gar keine Alternative. Denke du lieber nochmal drüber nach.

W.S.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Das könnte jetzt ein so schöner Programmiersprachenflamewar werden.

Die Voraussetzungen sind perfekt.

von Rolf M. (rmagnus)


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.

von Wilhelm M. (wimalopaan)


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).

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.