Forum: Compiler & IDEs typedef struct, *.h Datei - Problem


von Markus (Gast)


Lesenswert?

Hallo Leute!

Ich meine die Programmiersprache C gut zu beherschen, doch jetzt gibt 
mit das AVRStudio (GCC) einen Fehler aus, bei dem ich seit Tagen am 
verzweifeln bin. Folgendes Problem:

Ich habe zwei Header Dateien:

AnalogInputDriver.h
EEPROM_API.h

In der AnalogInputDriver.h steht folgendes:
1
#ifndef ANALOG_INPUT_DRIVER_H
2
#define ANALOG_INPUT_DRIVER_H
3
4
5
#include "Main.h"
6
7
/*** ENUM ALL ANALOGSTICK AXIS ***/
8
typedef enum eAnalogInputs
9
{
10
   L3_HORIZONTAL,       // Stick -100 to +100
11
   L3_VERTICAL,         // Stick -100 to +100
12
   R3_HORIZONTAL,       // Stick -100 to +100
13
   R3_VERTICAL,         // Stick -100 to +100
14
   L2,                  // Switch 0 to +100
15
   R2                   // Switch 0 to +100
16
}eAnalogInput;
17
18
/*** ENUM TYPES OF ANALOG RESULT ***/
19
typedef enum eAnalogReturnTypes
20
{
21
   CALIBRATED,
22
   NOT_CALIBRATED
23
}eAnalogReturnType;
24
25
/*** ENUM INVERTED STATES ***/
26
typedef enum eInvertStates
27
{
28
   INVERTED,
29
   NOT_INVERTED
30
}eInvertState;
31
32
/*** STRUCTS ***/
33
typedef struct sAnalogStickCalibValueSets
34
{
35
   signed short usMax;   
36
   signed short usNeutralMax;
37
   signed short usNeutralMin;
38
   signed short usMin;
39
   eInvertState InvertState;
40
}sAnalogStickCalibValueSet;
41
42
typedef struct sAnalogSwitchCalibValueSets
43
{
44
   signed short usMax;   
45
   signed short usMin;
46
}sAnalogSwitchCalibValueSet;
47
48
49
/*** GET AN ANALOG RESULT AS CALIBRATED VALUE OR AS NOT CALIBRATED VALUE ***/
50
void Init_AnalogInputDriver(void);
51
signed short getAnalogInput(eAnalogInput analogInput, eAnalogReturnType type);
52
53
54
#endif // ANALOG_INPUT_DRIVER_H

Dieser Teil Funktioniert auc wunderbar mit der zugehörigen 
AnalogInputDriver.c zusammen. So, nun möchte ich die Typen der 
Strukturen in EEPROM_API.h verwenden wie folgt:
1
#ifndef EEPROM_API_H
2
#define EEPROM_API_H
3
4
5
#include "Main.h"
6
#include "AnalogInputDriver.h"
7
8
9
unsigned char saveAnalogStickCalibValueSet(eAnalogInput analogInput, sAnalogStickCalibValueSet* pValueSet);
10
unsigned char loadAnalogStickCalibValueSet(eAnalogInput analogInput, sAnalogStickCalibValueSet* pValueSet);
11
12
unsigned char saveAnalogSwitchCalibValueSet(eAnalogInput analogInput, sAnalogSwitchCalibValueSet* pValueSet);
13
unsigned char loadAnalogSwitchCalibValueSet(eAnalogInput analogInput, sAnalogSwitchCalibValueSet* pValueSet);
14
15
16
17
18
#endif // EEPROM_API_H

Jedoch bekomme ich folgende Fehlermeldungen:

../EEPROM_API.h:9: error: expected ')' before 'analogInput'
../EEPROM_API.h:10: error: expected ')' before 'analogInput'
../EEPROM_API.h:12: error: expected ')' before 'analogInput'
../EEPROM_API.h:13: error: expected ')' before 'analogInput'

Weiß jemand warum dieser Fehler kommt? Eigentlich müsste das gehen, die 
Datentypen sind durch #include "AnalogInputDriver.h" bekannt und er gibt 
ja auch nicht aus, das er die Typen nicht kennt. Liegt das am AVRStudio?

Danke für jede Hilfe.

Gruß, Markus

von Hc Z. (mizch)


Lesenswert?

Deine EEPROM_API.h kompiliert mit obigen Codeteilen durch ohne sich zu 
beschweren.  Somit solltest Du vielleicht noch die Main.h nachliefern 
(für die habe ich eine leere Datei beim Test verwendet).  In den 
gezeigten Teilen ist kein Fehler (bis auf das fehlende #endif am Ende).

Nachtrag:

Markus schrieb:
> und er gibt
> ja auch nicht aus, das er die Typen nicht kennt.

Oh doch.  Er gibt aus, dass er eAnalogInput nicht kennt.  Sonst würde er 
dort keine schließende Klammer erwarten.  Er interpretiert eAnalogInput 
als untypisierten Parameternamen (was unsauber, aber nicht unzulässig 
wäre).

von Markus (Gast)


Lesenswert?

Also, ich hab jetzt das #include "Main.h" aus der Datei 
AnalogInputDriver.h rausgenommen und in die AnalogInputDriver.c 
reingemacht, jetzt gehts, allerdinsg würde mich interessieren warum es 
vorher nicht ging. Hier nochmal meine Main.h:
1
#ifndef MAIN_H
2
#define MAIN_H
3
4
/*********************/
5
/*** INCLUDE FILES ***/
6
/*********************/
7
#include <string.h>
8
9
#include "avr_compiler.h"
10
#include "port_driver.h"         // PORT driver
11
#include "clksys_driver.h"       // ClockSystem driver
12
#include "TC_driver.h"           // Timer driver
13
#include "adc_driver.h"
14
#include "usart_driver.h"
15
16
#include "SoundDriver.h"
17
#include "XBeeRingBufferSender.h"
18
#include "DisplayDriver.h"
19
#include "DisplayRingBufferSender.h"
20
#include "LED_Driver.h"
21
#include "AnalogInputDriver.h"
22
#include "DigitalInputDriver.h"
23
#include "EEPROM_API.h"
24
25
26
27
/***************************/
28
/*** FUNCTION PROTOTYPES ***/
29
/***************************/
30
void Init_ClockSystem(void);     // Adjust the CPU clock
31
void Init_PORT_Pins(void);      // IO Port init
32
void Init_Timer(void);           // Timer init
33
void Init_Interrupts(void);      // Interrupt init
34
void Init_ADC(void);             // ADC init
35
void Init_UART(void);
36
37
void Init_SoundDriver(void);
38
39
40
41
42
#endif // MAIN_H

PS: Was meinst Du mit "(bis auf das fehlende #endif am Ende)", es sind 
doch alle #endif´s da.

Danke schonmal für Deine Mühe.

Gruß, Markus

von Andreas B. (Gast)


Lesenswert?

Jetzt kann natürlich alles mögliche in den anderen Includes stecken. 
Vielleicht ist es einfacher, das ganze durch den Präprozessor zu jagen 
und dann anzuschauen, was der Compiler zu sehen bekommt. Da sieht man 
dann, ob ein #define irgendwo was kaputt gemacht hat.

Einfach den cpp auf die Quelldatei aufrufen, mit all den -I und -D 
Optionen, die auch dem gcc übergeben werden.

von Markus (Gast)


Lesenswert?

Ja, ich bin jetzt auch überzeugt, dass irgendwoe was mit nem #definde 
sein muss, da werd ich mich mal bei Gelegenheit dran setzen. Ich danke 
Euch erstmal für Eure Hilfe.

Gruß, Markus

von Ralf (Gast)


Lesenswert?

Scheinbar beherscht du die Sprache C doch nicht so gut! Denn sonst würde 
dir auffallen das du ein Problem bei den Includes hast.
Ich versuch das einmal zu erklären:
Wir arbeiten dein Programm von oben nach unten ab, angefangen bei der 
AnalogInputDriver.h.

Hiermit sorgst du dafür, das die Datei nur einmal durchkompiliert wird: 
#ifndef ANALOG_INPUT_DRIVER_H
#define ANALOG_INPUT_DRIVER_H

Als nächstes wird die main.h includiert, wir springen also in die 
main.h.

Dort werden wieder verschiedene Header includiert und an der 15. Stelle 
die EEPROM_API.h. Wir springen in die EEPROM_API.h.

Dort versuchst du die AnalogInputDriver.h zu includieren, dies geht aber 
nicht weil du schon "ANALOG_INPUT_DRIVER_H" definiert hast. Der 
Compieler verlässt die AnalogInputDriver.h und arbeitet die EEPROM_API.h 
weiter ab.

Jetzt versuchst du deine typedefs zu verwenden, die aber noch nicht 
kompiliert wurden. Weil du über die Zeile "#include "Main.h" deine 
Header-Datei verlassen hast.

Du hast 2 Möglichkeiten das Problem zu umgehen:
1. die Main.h nicht includieren.
2. aus der Main.h die Zeile "#include "EEPROM_API.h" löschen.

von Karl H. (kbuchegg)


Lesenswert?

Ralf schrieb:

> Du hast 2 Möglichkeiten das Problem zu umgehen:
> 1. die Main.h nicht includieren.
> 2. aus der Main.h die Zeile "#include "EEPROM_API.h" löschen.


@TO

Diese Rundumschlag-'1 Header includiert alles andere' sind meistens 
sowieso keine gute Idee. Jetzt hast du am eigenen Leib erfahren warum.
So wird es nämlich immer schwieriger zu überblicken wer wen in welcher 
Reihenfolge includiert.

von Hc Z. (mizch)


Lesenswert?

Markus schrieb:
> PS: Was meinst Du mit "(bis auf das fehlende #endif am Ende)", es sind
> doch alle #endif´s da.

Stimmt.  Beim Copy&Paste zum Testkompilieren hatte ich eine Zeile zu 
wenig selektiert und habe diesen Mangel dann Dir zugerechnet. 
Fälschlicherweise.

von Markus (Gast)


Lesenswert?

@Ralf: Du hast natürlich vollkommen recht, woran der Fehler lag ist mir 
jetzt auch klar geworden. Aber wie das eben so ist, gerade wenn man eine 
Sprache lange nicht implementiert hat, sieht man manchmal den Wald vor 
lauter Bäumen nicht, und das passiert Dir sicher auch ab und zu, daher 
sollte man gerade mit provokanten Aussagen aufpassen ;-).

@All: Ich danke Euch allen für die schnelle und kopentente Hilfe. Ich 
räum jetzt mal meine includes auf.

Grüße,
Markus

von klaus (Gast)


Lesenswert?

Karl heinz Buchegger schrieb:
> Diese Rundumschlag-'1 Header includiert alles andere' sind meistens
> sowieso keine gute Idee. Jetzt hast du am eigenen Leib erfahren warum.
> So wird es nämlich immer schwieriger zu überblicken wer wen in welcher
> Reihenfolge includiert.

Hallo Karl Heinz,

hast du einen Tipp: Wie organisierst du header files ?

Viele Grüße
Klaus

von Klaus (Gast)


Lesenswert?

Naja, im Prinzip ist es ganz einfach: Jede Datei includiert nur genau 
das, was die unbedingt braucht, um kompilierbar zu sein.

Außerdem ist es sinnvoll, dass die .c Dateien ihre zugehörige .h Datei 
auch einbinden.

Das heißt:
-deine main.h fliegt komplett raus (es sei denn da sind irgendwelche 
defines drin, die global gebraucht werden. Dann nenn ich die Datei aber 
meisten config.h oder ähnliches).

- AnalogInputDriver.h includiert gar nichts.

- EEPROM_API.h includiert AnalogInputDriver.h

von Klaus W. (mfgkw)


Lesenswert?

Ich will mir jetzt KHBs Identität anmaßen, aber ich mache es in
aller Regel so:

Alles, was thematisch eng zusammengehört und in sich abgeschlossen
ist (je nach Fall) kommt in ein Paar von .c und .h.
Es gibt also eine 1:1-Entsprechung von Definitionen (Funktionen,
notfalls globale Variablen) in einer .c und den restlichen
Dingen in der .h (Funktionsdeklarationen, Typdefinitionen,
Konstanten, Makros).
Welchen Umfang jetzt in einem .c/.h-Paar steckt, ist natürlich
Ermessenssache bzw. Erfahrung. Da kann man wenig vorschreiben.
Interessant ist immer, wie universell+wiederverwendbar ein
solches Paar ist.

Die .c includet immer die eigene .h.

In der .h werden weiterhin alle anderen .h includet, die
darin nötig sind (zugrundeliegende Typdefinitionen besonders).

So braucht man in den .c in der Regel nur wenig includen, weil
die weiter unten benutzten Spielerein schon von den .h abgehakt
werden.

Zudem hat natürlich jede .h das Spiel mit
#ifndef...#define...#endif.

Eine globale .h für alles (wie eine main.h) braucht man
eigentlich selten, meistens hat man dann etwas falsch gemacht.

Mit diesen Regeln sollte man in den allermeisten Fällen gut
zurecht kommen.
Von Fall zu Fall kann man natürlich auch davon abweichen, aber
nur wenn man einen guten Grund hat.

Bei Bedarf kann man mehrere kompilierte .c zu einer Lib
zusammenfassen, dann macht es oft auch Sinn, die
zugehörigen Header in einer neuen .h mit #include
zusammenzufassen.

von Klaus W. (mfgkw)


Lesenswert?

oh, da war der andere Klaus wieder schneller.

von StinkyWinky (Gast)


Lesenswert?

Ich gebe Klaus vollkommen recht.

Allerdings habe ich mir angewöhnt, in den h-Files grundsätzlich keine 
anderen Files zu includen. Das überlasse ich komplett den c-files.

von Karl H. (kbuchegg)


Lesenswert?

StinkyWinky schrieb:
> Ich gebe Klaus vollkommen recht.
>
> Allerdings habe ich mir angewöhnt, in den h-Files grundsätzlich keine
> anderen Files zu includen. Das überlasse ich komplett den c-files.

Das solltest du umgewöhnen.
Wenn in einem h-File ein anderer Datentyp gebraucht wird (zb um einen 
Funktionsprototypen zu schreiben), und eine Forward-Deklaration nicht 
reicht, dann soll das Header-File den zusätzlichen Header ebenfalls 
inkludieren.

Bürde niemals demjenigen, der ein bestimmtes Header File includieren 
möchte die Arbeit auf, sich daran zu erinnern, dass er dazu vorher ein 
anderes Header File inkludieren muss. Und bei einer Forward Deklaration 
ist man dann auch so nett und schreibt einen Kommentar in den Header, wo 
der eigentliche Datentyp zu finden ist. Das Ziel ist es, dass man nur 
ein Header File für eine Funktionalität inkludieren muss und dann hat 
man automatisch alles inkludiert, was für diese Funktionalität gebraucht 
wird.

Wenn du dafür sorgst, dass alle Header in sich vollständig sind, aber 
auch nicht einfach alles andere inkludieren was sie gar nicht brauchen, 
dann erweist sich das in der Praxis als der gangbarste Weg, mit den 
wenigsten Schwierigkeiten.

von klaus (Gast)


Lesenswert?

Bei meinen Projekten habe ich es immer so gemacht, dass es eine einzige 
Header file gibt, die alles inkludiert. Alle Module (Header und Source 
Files) binden dann nur noch diesen Header ein.

Hat Vor- und Nachteile.

Vorteile:

- Man sieht sofort in welcher Reihenfolge includes vorgenommen werden 
und wie es der Compiler zu sehen bekommt
- Wird ein Modul umbenannt muss man den Namen nur in dieser einen Header 
File ändern und sonst nirgends, selbst wenn das Modul von 100 anderen 
Modulen genutzt wird (insbesondere: denkt man an Versionierung würden 
sich dadurch 100 Files ändern, so nur eine).

Nachteile:

- Compilieren kann etwas länger dauern
- Die Information welche includes überhaupt benötigt werden (eigentlich 
eine Eigenschaft des jeweiligen Moduls) ist nicht mehr direkt erkennbar

von Karl H. (kbuchegg)


Lesenswert?

klaus schrieb:

> - Wird ein Modul umbenannt muss man den Namen nur in dieser einen Header
> File ändern und sonst nirgends, selbst wenn das Modul von 100 anderen
> Modulen genutzt wird (insbesondere: denkt man an Versionierung würden
> sich dadurch 100 Files ändern, so nur eine).

Gerade dann, wenn du 100 andere Module hast, ist das was du machst genau 
das was du nicht tun solltest.

Denn damit hebelst du den make-Mechanismus komplett aus, der nur die 
Dinge nachcompiliert, die sich tatsächlich geändert haben. Ein 
Header-File ändern und schon muss alles nachcompiliert werden.


Was bei 3 oder 4 Modulen noch eine verzeichliche Nachlässigkeit ist, 
wird immer mehr zum Boomerang, je größer die Projekte werden. Bei 3 oder 
4 Modulen gibt es aber ohnehin noch keine Reihenfolge-Schwierigkeiten.

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.