Forum: Mikrocontroller und Digitale Elektronik Compilerfehler nach Auslagern von Funktionen.


von Giuseppe B. (brungi)


Lesenswert?

Hallo Leute,

ich hab mal wieder ein Problem.

Ich habe einen funktionierenden Code dahingehend geändert, Dass ich 
Graphik-Funktionen, die bisher in der main.c waren in eine graphics.c 
ausgelagert habe, die entsprechenden Funktionen dann in der includierten 
graphics.h deklariert. Soweit alles gut.

Dann habe ich das gleiche mit der ( so ziemlich) einzigen adc-Funktion 
gemacht. Also Funktion in die adc.c und dann eben in der adc.h 
deklariert.

Nun bringt der Compiler den Fehler:
1
adc.c:3:error: expected '=', ',', ';', 'asm' or '__attribute__' before 'ReadChannel'

Wenn man die adc.c weglässt, und alles in  die adc.h schreibt 
funktioniert es. ( Soll ja aber nicht sein, daß Funktionen in einer *.h 
sind...- wie ich ja vergangene Woche erst gelernt hab)

Die adc.h wird in der main.c includiert.

1
 
2
// adc.h
3
extern uint16_t adc_value;
4
extern uint16_t ReadChannel(uint8_t mux_adc_channel);
1
// adc.c
2
3
uint16_t adc_value;
4
uint16_t ReadChannel(uint8_t mux_adc_channel) 
5
{
6
        
7
// der Übersicht halber ist die Funktion hier amputiert
8
9
}

Die Vorgehensweise habe ich bei:

http://openbook.galileocomputing.de/c_von_a_bis_z/009_c_funktionen_019.htm#mj58913b45fd4f8e5cbad7632da152ca54

abgeguckt :)

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Und, wird in adc.c überhaupt irgendwas includiert?

Vermutlich fehlt stdint.h, und der Compiler weiß nicht, was uint16_t ist 
...

von EGS_TI (Gast)


Lesenswert?

Zeig die kompletten Dateien.

von .... (Gast)


Lesenswert?

vielleicht die adc.h vor der inttypes.h includiert?

von Rolf Magnus (Gast)


Lesenswert?

Wo ist denn das #include <stdint.h>, das du für uint16_t brauchst?

von Giuseppe B. (brungi)


Lesenswert?

@ Rufus

Das war´s!
In der adc.c wurde gar nix includiert. Habe nun <stdint.h>, <avr/io.h>, 
<stdlib.h> includiert. Jetzt baut er es.

Danke für die Antworten !

von Giuseppe B. (brungi)


Angehängte Dateien:

Lesenswert?

Hier jetzt mal die kompletten Dateien...

Ich hatte aber die <stdint.h> bzw. <inttypes.h>  bisher überhaupt nicht 
includiert. Und es hat trotzdem funktioniert.

von tobi (Gast)


Lesenswert?

du solltest in deinen *.c dateien auch die jeweiligen *.h includieren

also in adc.c

#include "adc.h"

von Konrad S. (maybee)


Lesenswert?

Du solltest in der adc.c dringend auch adc.h includieren, entsprechend 
auch für graphics.*. Dadurch verhinderst du (bzw. der Compiler), dass 
sich Header-Datei und Code-Datei versehentlich auseinander entwickeln.

In der graphics.c steht vor den <...>-Includes ein Include mit "...". 
Das kann im Einzelfall OK sein, ist aber eher eine mögliche 
Fehlerquelle.
Edit: auch in der main.c

von Giuseppe B. (brungi)


Lesenswert?

@ tobi  &&  @ Konrad

das mit dem Includieren der *.h in den *.c führt leider wieder zum 
ursprünglichen Compiler-Fehler.

Ich habe nun die Reihenfolge der Includes geändert..

erst die vom compiler <bla>
dann die eigenen "bla"

von xfr (Gast)


Lesenswert?

Giuseppe B. schrieb:
> Ich habe nun die Reihenfolge der Includes geändert..
>
> erst die vom compiler <bla>
> dann die eigenen "bla"

Und dann gehts? Dann fehlt in einem Deiner eigenen Header noch etwas ...

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Giuseppe B. schrieb:
> das mit dem Includieren der *.h in den *.c führt leider wieder zum
> ursprünglichen Compiler-Fehler.

Dann machst Du da noch irgendwas falsch.

Die adc.h muss sich in adc.c einbinden lassen, und zwar als letzte 
#include-Datei, nach allen anderen #include-Anweisungen und vor dem in 
adc.c enthaltenem Code.

Wenn es da immer noch Fehlermeldungen gibt, poste exakt die 
Headerdatei, die C-Datei und die Fehlermeldung(en).

von xfr (Gast)


Lesenswert?

Rufus Τ. Firefly schrieb:
> Die adc.h muss sich in adc.c einbinden lassen, und zwar als letzte
> #include-Datei, nach allen anderen #include-Anweisungen und vor dem in
> adc.c enthaltenem Code.

Wenn man es richtig macht, sollte man sie doch sogar als allererste 
einbinden können. Ist der beste Test, um zu sehen, ob noch etwas fehlt.

von Giuseppe B. (brungi)


Lesenswert?

@ Rufus

Nicht ganz so... es ging nachdem ich die <stdint.h>, <avr/io.h> und
<stdlib.h> in die adc.c includiert hatte.

seit dem Includieren der entsprechenden *.h ind die *.c kommt wieder 
folgender Fehler:
1
In file included from graphics.c:37:
2
graphics.h:3: error: expected '=', ',', ';', 'asm' or '__attribute__' before 'u8g'
3
make: *** [graphics.o] Fehler 1

Diesmal bzgl. der graphics.h .

Dann noch die "u8g.h" in die graphics.h includiert. Jetz geht´s wieder.

Dass man aber *.h in *.h includieren darf stand so nicht im ebook :)

Und noch ne Frage:
Wenn in der Makefile steht...
1
z.B. SRC: = *.c
...
Was baut dann der Compiler zuerst, bzw. macht er die main.c zuerst oder 
zuletzt ? Oder macht er es alphabetisch ?

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Giuseppe B. schrieb:
> Dann noch die "u8g.h" in die graphics.h includiert. Jetz geht´s wieder.

Es mag zwar gehen, ist aber murks.

von Konrad S. (maybee)


Lesenswert?

xfr schrieb:
> Wenn man es richtig macht, sollte man sie doch sogar als allererste
> einbinden können. Ist der beste Test, um zu sehen, ob noch etwas fehlt.

Das sehe ich anders. Da müsste die xxx.h sämtliche Header einbinden, die 
auch die xxx.c zum Übersetzen braucht. Das tut evtl. bei einem 
AVR-Projekt wegen der überschaubaren Größe nicht weh. Bei einem richtig 
großen Projekt wirst du keine Freude daran haben, weil - egal welchen 
Header du änderst - du immer das gesamte Projekt neu kompilieren musst.

von Konrad S. (maybee)


Lesenswert?

Rufus Τ. Firefly schrieb:
> Giuseppe B. schrieb:
>> Dann noch die "u8g.h" in die graphics.h includiert. Jetz geht´s wieder.
>
> Es mag zwar gehen, ist aber murks.

Exakt. Siehe mein Beitrag von eben.

von xfr (Gast)


Lesenswert?

Giuseppe B. schrieb:
> Was baut dann der Compiler zuerst, bzw. macht er die main.c zuerst oder
> zuletzt ? Oder macht er es alphabetisch ?

Wahrscheinlich alphabetisch. Spielt aber keine Rolle, da alle c-Dateien 
unabhängig voneinander kompiliert werden und erst danach vom Linker zu 
einem Programm zusammengesetzt werden.

Man darf übrigens nicht nur h-Dateien in h-Dateien inkluden, es ist 
dringend zu empfehlen! Jede Datei sollte in sich vollständig sein. Die 
Vorgehensweise ist im Prinzip, jede Datei Zeile für Zeile durchzugehen. 
Jedes Wort, das nicht weiter oben in der gleichen Datei deklariert ist 
oder zur C-Programmiersprache gehört, kann nur aus einer anderen 
inkludierten Datei stammen. Die muss dann eingebunden werden.

von xfr (Gast)


Lesenswert?

Konrad S. schrieb:
> Das sehe ich anders. Da müsste die xxx.h sämtliche Header einbinden, die
> auch die xxx.c zum Übersetzen braucht. Das tut evtl. bei einem
> AVR-Projekt wegen der überschaubaren Größe nicht weh. Bei einem richtig
> großen Projekt wirst du keine Freude daran haben, weil - egal welchen
> Header du änderst - du immer das gesamte Projekt neu kompilieren musst.

Nein, xxx.h muss nur sämtliche Header einbinden, auf deren Teile 
irgendwo in xxx.h verwiesen wird. Alle Header, die in xxx.c gebraucht 
werden (aber nicht in xxx.h) bindet man natürlich auch nur in xxx.c ein.

von uC W. (ucwriter)


Lesenswert?

Mal ein Beispiel

du hast einen header Vector.h, in dem z.B. steht
1
typedef struct
2
{
3
   uint8_t x;
4
   uint8_t y;
5
   uint8_t z;
6
} Vector_t;

In dem struct findest Du den Datentyp uint8_t. Jetzt könnte man noch die 
stdint.h vor dem typedef noch im inkludieren, damit der Compiler weiss 
was ein uint8_t. Das macht man oder besser gesagt das mache ich nicht. 
In meinem vektor.c würde ich erst alles inkludieren was mit spitzen 
Klammern zu tun hat, und erst dann meine header.

Warum? Es ist für mich eine reine Deklaration. Ich möchte hier noch 
nicht sagen, was ein uint8_t ist. Das ganze wird dann portabler. Zum 
Anderen, wenn sich die uint8_t nicht mehr in stdint.h befinden würde, 
bräuchtest Du das header file trotzdem nicht anfassen. Ok. das ein 
uint8_t sich nicht mehr in stdint.h befindet, mag ein blödes beispiel 
sein.

von Giuseppe B. (brungi)


Angehängte Dateien:

Lesenswert?

Ich habe zwischenzeitlich ziemlich viele Includes entfernt.
Im Anhang nochmals alle Files im aktuellen Zustand.

Es hat wohl daran gelegen, daß in der graphics.c, welche hauptsächlich 
mit der u8glib arbeitet, die "u8g.h" nicht includiert war.

Dass sich das Problem zu Anfangs aud die adc.c ausgewirkt lag wohl an 
der fehlenden <io.h> ???

von xfr (Gast)


Lesenswert?

uC Writer schrieb:
> Mal ein Beispiel
>
> du hast einen header Vector.h, in dem z.B. steht
> typedef struct
> {
>    uint8_t x;
>    uint8_t y;
>    uint8_t z;
> } Vector_t;
>
> In dem struct findest Du den Datentyp uint8_t. Jetzt könnte man noch die
> stdint.h vor dem typedef noch im inkludieren, damit der Compiler weiss
> was ein uint8_t. Das macht man oder besser gesagt das mache ich nicht.
> In meinem vektor.c würde ich erst alles inkludieren was mit spitzen
> Klammern zu tun hat, und erst dann meine header.
>
> Warum? Es ist für mich eine reine Deklaration. Ich möchte hier noch
> nicht sagen, was ein uint8_t ist. Das ganze wird dann portabler. Zum
> Anderen, wenn sich die uint8_t nicht mehr in stdint.h befinden würde,
> bräuchtest Du das header file trotzdem nicht anfassen. Ok. das ein
> uint8_t sich nicht mehr in stdint.h befindet, mag ein blödes beispiel
> sein.

Und wie siehst Deine vektor.c aus? Dort muss ein konkreter Typ bekannt 
sein. Also musst Du dort stdint.h einbinden. Damit ist dann aber auch 
festgelegt, was die Implementierung der Funktionen für einen Typ 
erwartet, nämlich ein unsigned char.

Wenn jetzt ein Benutzer der Bibliothek auf die Idee kommt, uint8_t vor 
dem Einbinden Deiner vektor.h anders zu definieren als in stdint.h, 
stimmen Deklaration in vektor.h und Implementierung in vektor.c nicht 
mehr überein und es kracht.

Wenn die Implementierung (vektor.c) mit anderen Datentypen umgehen soll, 
dann muss in vektor.c ein entsprechender Header (z.B. vektor_type.h) 
eingebunden sein. Es reicht nicht, dass der Benutzer das in seinem 
Programm macht! Und damit wären wieder beim Anfang: Dieser Header wird 
sowohl in vektor.c als auch im Programm des Benutzers gebraucht und muss 
gleich sein, also packt man ihn sinnigerweise gleich in vektor.h.

von Konrad S. (maybee)


Lesenswert?

xfr schrieb:
> Giuseppe B. schrieb:
>> Was baut dann der Compiler zuerst, bzw. macht er die main.c zuerst oder
>> zuletzt ? Oder macht er es alphabetisch ?
>
> Wahrscheinlich alphabetisch. Spielt aber keine Rolle, da alle c-Dateien
> unabhängig voneinander kompiliert werden und erst danach vom Linker zu
> einem Programm zusammengesetzt werden.

Die Reihenfolge wird durch die im Makefile formulierten Abhängigkeiten 
festgelegt.

von Konrad S. (maybee)


Lesenswert?

@xfr
Deine Argumentation bezüglich Includes ist schon richtig. Aber meine 
Erfahrung bei großen Projekten brachte mich zur Ansicht, dass es eine 
gute Idee ist, wenn ein Header nur das Interface verkauft, für das er 
selbst zuständig ist. Daher findet sich in meinen Headern dann sowas:
1
struct binwoanderszuhause;
2
int funktionsowieso( binwoanderszuhause *x );
Den Header mit den Details zu struct binwoanderszuhause ziehen sich nur 
die Code-Dateien rein, die es nötig haben.

von uC W. (ucwriter)


Lesenswert?

@xfr

Wenn der Benutzer auf die Idee käme uint8_t selber zu deklarieren? Na 
dann hat derjenge noch ein paar andere Leichen im Keller.

Und wenn man uint8_t selber deklariert und danach die stdint inkludiert, 
in der dann uint8_t nochmals deklariert wird, sollte es ein 
Compiler-Fehler geben. Zumindest habe ich das noch nicht geschafft. 
Vielleicht ein Beispiel?

Und es sollte natürlich klar sein, dass ich in meiner vektor.c die 
stdint.h inkludiere. Zumindest bei mir geht das mit
1
#include <stdint.h>
also die Files mit den spitzen Klammern.
Mach es aber wie Du magst.

von xfr (Gast)


Lesenswert?

Konrad S. schrieb:
> @xfr
> Deine Argumentation bezüglich Includes ist schon richtig. Aber meine
> Erfahrung bei großen Projekten brachte mich zur Ansicht, dass es eine
> gute Idee ist, wenn ein Header nur das Interface verkauft, für das er
> selbst zuständig ist. Daher findet sich in meinen Headern dann sowas:
>
> struct binwoanderszuhause;
> int funktionsowieso( binwoanderszuhause *x );
>
> Den Header mit den Details zu struct binwoanderszuhause ziehen sich nur
> die Code-Dateien rein, die es nötig haben.

In dem Fall kann man sich drüber streiten. Wenn jemand funktionsowieso() 
verwenden will, braucht er die Definition von struct binwoanderszuhause. 
Also kann man sie ihm auch gleich mitgeben. Es ist ja (wie oben 
beschrieben) nicht möglich, dass sich der Benutzer aussucht, wie struct 
binwoanderszuhause auszusehen hat, sondern er muss genau die Version 
nehmen, die auch der Implementierung von funktionsowieso() zu Grunde 
liegt.

In Deinem Beispiel kompiliert das Programm aber immerhin, da struct 
binwoanderszuhause ja zumindest deklariert wurde. In dem Beispiel mit 
uint8_t gibt es dagegen Compilerfehler.

von xfr (Gast)


Lesenswert?

uC Writer schrieb:
> Wenn der Benutzer auf die Idee käme uint8_t selber zu deklarieren? Na
> dann hat derjenge noch ein paar andere Leichen im Keller.

Genau das wolltest Du doch aber erreichen:

> Warum? Es ist für mich eine reine Deklaration. Ich möchte hier noch
> nicht sagen, was ein uint8_t ist.

Wenn Du nicht sagst, was ein uint8_t ist und der Benutzer es auch nicht 
sagen soll, wer bleibt denn dann noch?

> Und wenn man uint8_t selber deklariert und danach die stdint inkludiert,
> in der dann uint8_t nochmals deklariert wird, sollte es ein
> Compiler-Fehler geben. Zumindest habe ich das noch nicht geschafft.
> Vielleicht ein Beispiel?

Genau. Dadurch dass man stdint.h in vektor.h inkludiert, gibt es einen 
Fehler, wenn der Benutzer versuchen würde, uint8_t anders zu 
deklarieren. Wenn in vektor.h dagegen nichts von stdint.h steht, hält 
den Benutzer niemand davon ab. Und genau das ist gefährlich.

> Und es sollte natürlich klar sein, dass ich in meiner vektor.c die
> stdint.h inkludiere. Zumindest bei mir geht das mit#include <stdint.h>
> also die Files mit den spitzen Klammern.
> Mach es aber wie Du magst.

Damit ist dann Dein zweites Argument auch keins:

> Zum Anderen, wenn sich die uint8_t nicht mehr in stdint.h befinden würde,
> bräuchtest Du das header file trotzdem nicht anfassen. Ok. das ein
> uint8_t sich nicht mehr in stdint.h befindet, mag ein blödes beispiel
> sein.

Denn wenn sich uint8_t nicht mehr in stdint.h befinden würde, müsstest 
Du Deine vektor.c anfassen. Und nicht nur die, sondern auch alle 
Programme, die Deine Header-Datei mit uint8_t ohne Einbinden von 
stdint.h verwenden. Wenn Du es dagegen in vektor.h hättest, bräuchst Du 
es nur an dieser einen Stelle ändern.

von Konrad S. (maybee)


Lesenswert?

xfr schrieb:
> Wenn jemand funktionsowieso()
> verwenden will, braucht er die Definition von struct binwoanderszuhause.

Nicht unbedingt. Beispiel: FILE *

Wenn du dir z.B. Microsofts Umgang mit FILE-Pointern anschaust, dann 
hast du ein Beispiel, dass es auch möglich ist, in unterschiedlichen 
Kontexten (z.B. DLLs) mit unterschiedlichen Implementierungen der struct 
FILE zu arbeiten. Du darfst diesen FILE-Pointer zwar in andere Kontexte 
weiterreichen, musst aber sicherstellen, dass er nur in "geeigneten" 
Kontexten verwendet wird. So können unterschiedliche 
FILE-Implementierungen innerhalb einer Applikation koexistieren, ohne 
einen Crash zu verursachen. OK, klar ist das irgendwo zwischen "nicht 
unbedingt empfehlenswert" und "absoluter Schwachsinn" anzusiedeln, aber 
so kann Uralt-Software auf neueren Systemen weiterverwendet werden.

Ich verwende diese Technik des "nur das Interface beschreiben" gerne für 
eigenständige Subsysteme. Der Nutzer eines solchen Subsystems ist 
vollständig unabhängig von der Implementierung des Subsystems. Solange 
sich das vom Nutzer verwendete Interface nicht ändert - es kann aber 
erweitert werden - muss er bei einer neuen Implementierung des 
Subsystems seinen Quellcode nicht neu übersetzen. Das Subsystem ist 
somit einfach austauschbar. Eber das Prinzip der DLL bzw. Shared 
Objects.

von xfr (Gast)


Lesenswert?

Mit Microsofts FILE-Pointer kenne ich mich leider nicht aus. Kannst Du 
es kurz erklären? Gibt es da quasi eine file_old.h und file_new.h und 
manche DLLs wollen ein FILE gemäß file_old.h und manche nach file_new.h? 
Und ich als Anwender muss die richtige file_*.h einbinden, je nachdem 
mit welcher DLL ich rede? Das wäre imo gerade ein Anti-Beispiel. Kann 
mir gerade nicht vorstellen, wie man innerhalb einer c-Datei 
verschiedene Varianten von FILE-Pointern nebeneinander verwenden kann.

von Konrad S. (maybee)


Lesenswert?

xfr schrieb:
> Mit Microsofts FILE-Pointer kenne ich mich leider nicht aus.

Du Glücklicher!
Aber immerhin bezahlen sie mich dafür - Schmerzensgeld also. ;-)

> Kannst Du
> es kurz erklären? Gibt es da quasi eine file_old.h und file_new.h und
> manche DLLs wollen ein FILE gemäß file_old.h und manche nach file_new.h?
> Und ich als Anwender muss die richtige file_*.h einbinden, je nachdem
> mit welcher DLL ich rede?

Nein, du inkludierst immer nur stdio.h. Du wählst nur die 
Compileroptionen, die dir am wenigsten grausam vorkommen. Das hat dann 
Einfluss auf Speichermodell, Aufrufkonventionen und die Schuhgröße des 
Anwenders, bei dem die Applikation nicht crasht.

> Kann
> mir gerade nicht vorstellen, wie man innerhalb einer c-Datei
> verschiedene Varianten von FILE-Pointern nebeneinander verwenden kann.

Nicht innerhalb einer C-Datei, nur innerhalb einer Applikation. Z.B. 
kann die x.exe mit einer anderen Vorstellung von FILE kompiliert sein 
als die y.dll, die von der x.exe eingebunden wird. Wenn y.dll einen 
FILE-Pointer an die x.exe herausrückt und x.exe damit arbeitet, dann 
crasht es eben. x.exe kann mit diesem FILE-Pointer aber problemlos eine 
Funktion von y.dll aufrufen, die mit dem FILE-Pointer arbeitet. Das 
funktioniert deshalb, weil jede DLL und EXE gegen eine bestimmte libc 
(bzw. msvcrtschlagmichtot.xyz) gelinkt ist - wobei "funktionieren" hier 
großzügig ausgelegt werden muss.

von xfr (Gast)


Lesenswert?

Konrad S. schrieb:
> Nicht innerhalb einer C-Datei, nur innerhalb einer Applikation. Z.B.
> kann die x.exe mit einer anderen Vorstellung von FILE kompiliert sein
> als die y.dll, die von der x.exe eingebunden wird. Wenn y.dll einen
> FILE-Pointer an die x.exe herausrückt und x.exe damit arbeitet, dann
> crasht es eben. x.exe kann mit diesem FILE-Pointer aber problemlos eine
> Funktion von y.dll aufrufen, die mit dem FILE-Pointer arbeitet. Das
> funktioniert deshalb, weil jede DLL und EXE gegen eine bestimmte libc
> (bzw. msvcrtschlagmichtot.xyz) gelinkt ist - wobei "funktionieren" hier
> großzügig ausgelegt werden muss.

Okay, glaube das habe ich einigermaßen verstanden. Man bindet also in 
x.exe eigentlich ein andere Definition von FILE ein, als y.dll verwendet 
hat. Da ein Pointer aber immer gleich groß ist, kann man einen 
FILE-Pointer, den man von y.dll bekommen hat bei sich speichern und 
wieder an y.dll zurückgeben. Erst wenn man FILE derefenzieren und an den 
Daten etwas ändern würde, käme Schrott bei raus?

Das würde aber doch auch noch "funktionieren", wenn in y.h stdio.h 
eingebunden wäre, oder? Es wäre dann zwar die falsche Version (für 
y.dll), aber wenn man nur x.exe kompiliert und nicht y.dll (was wegen 
der falschen Definition eh scheitern dürfte), hat man das gleiche 
Ergebnis. Welche Definition von FILE für das aktuelle Programm verwendet 
wird, legt man ja anscheinend durch die Compilereinstellungen fest und 
nicht durch den Namen der h-Datei.

Davon abgesehen würde ich so ein System absolut nicht als Vorbild für 
den Entwurf von neuer Software sehen ... ;-)

von Konrad S. (maybee)


Lesenswert?

xfr schrieb:
> Erst wenn man FILE derefenzieren und an den
> Daten etwas ändern würde, käme Schrott bei raus?

Ganz genau! Solange man den FILE-Pointer nur als Magic Cookie (ahem - 
"Handle") weiterreicht, ist die Welt in Ordnung.

> Das würde aber doch auch noch "funktionieren", wenn in y.h stdio.h
> eingebunden wäre, oder? Es wäre dann zwar die falsche Version (für
> y.dll), aber wenn man nur x.exe kompiliert und nicht y.dll (was wegen
> der falschen Definition eh scheitern dürfte), hat man das gleiche
> Ergebnis. Welche Definition von FILE für das aktuelle Programm verwendet
> wird, legt man ja anscheinend durch die Compilereinstellungen fest und
> nicht durch den Namen der h-Datei.

Sofern die Details der struct FILE in stdio.h enthalten sein sollten - 
wozu? - müsste sie sich eben durch die Copileroptionen Makro-gesteuert 
anpassen.

> Davon abgesehen würde ich so ein System absolut nicht als Vorbild für
> den Entwurf von neuer Software sehen ... ;-)

Als aufwendiges Beispiel für "Wie schieße ich mir ohne Colt in den Fuß?" 
taugt es allemal. ;-)

von xfr (Gast)


Lesenswert?

Es ging ja ursprünglich darum, ob man in seinen Headerdateien die 
anderen Headerdateien, auf die sich das Interface bezieht, mit einbinden 
soll oder nicht. Weiter oben habe ich ein paar Gründe genannt, die dafür 
sprechen und bisher keinen gelesen, der dagegen spricht.

Also ob ich als Entwickler von meinmodul.c folgenden Header schreiben 
soll:
1
// meinmodul.h
2
3
void tu_was(uint16_t bla);
4
void lese_datei(FILE* file);

Dann muss mein Hauptprogramm zwingend so aussehen:
1
// main.c
2
3
#include <stdint.h>
4
#include <stdio.h>
5
#include "meinmodul.h"

Es muss es in der Reihenfolge da stehen und ich muss stdint.h und 
stdio.h einbinden, obwohl ich selber vielleicht gar nichts daraus nutze. 
Außerdem muss ich wissen, dass uint16_t aus stdint.h kommt und FILE aus 
stdio.h. Gut, hier kann man das voraussetzen, wenn es aber keine 
Standard-Header sind nicht.

Die aus meiner Sicht immer bessere Variante ist dagegen:
1
// meinmodul.h
2
3
#include <stdint.h>
4
#include <stdio.h>
5
6
void tu_was(uint16_t bla);
7
void lese_datei(FILE* file);

Dann reicht im Hauptprogramm:
1
// main.c
2
3
#include "meinmodul.h"

Ebenso funktioniert:
1
// main.c
2
3
#include "meinmodul.h"
4
#include <stdint.h>

Oder natürlich auch die Variante von oben. Nur wenn ich im Hauptprogramm 
falsche Headerdateien einbinde, die eine andere Definition mit gleichem 
Namen enthalten oder Dinge neu definiere, die im Interface benutzt 
werden, bekomme ich zu Recht einen Compilerfehler.

von Konrad S. (maybee)


Lesenswert?

Gegen das Einbinden von "<...>"-Headern (also "System"-Header) in 
eigenen h-Dateien spricht mMn nichts, da man diese Sorte Header als 
statisch ansehen darf und diese Header immer (hoffentlich?!?) gegen 
unerwünschte Nebenwirkungen durch mehrfaches Inkludieren abgesichert 
sind. Den Fall von Kernel-naher Entwicklung unter Linux mit 
dazwischenliegendem Kernel-Update lasse ich mal außer Acht.

Wenn die '"..."'-Header ordentlich gemacht sind - Absicherung gegen 
mehrfaches Inkludieren - spricht technisch gesehen auch nichts dagegen. 
Ich versuche allerdings, dass ich eben gerade keine dieser Header in 
die h-Datei aufnehmen muss; ich vermeide es, wenn es mit sinnvollem 
Aufwand machbar ist. Ich will in die c-Datei nur das allernötigste 
reinziehen und ich will keine unnötigen Abhängigkeiten schaffen. Ich bin 
also kein Anhänger der "global.h"-Technik, bei der man das gesamte 
Projekt neu kompiliert, nur weil sich in einem einzigen Header eine 
Kleinigkeit geändert hat. Aber es ist eine Frage des Geschmacks, über 
die sich streiten lässt - richtig und falsch sind hier einfach 
unpassende Attribute.

So etwas hingegen
1
#include "meinmodul.h"
2
#include <stdint.h>
halte ich für gefährlich. Je nach Mitteilsamkeit des Compilers kann die 
Fehlersuche ausarten. Als problematisch sehe ich hier die Verstöße gegen 
reservierte Namen(sbereiche) an.

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.