Forum: Mikrocontroller und Digitale Elektronik header in mehreren c-files


von Jens (Gast)


Lesenswert?

Hallo,
ich möchte mit einem Attiny2313 in c eine Uhr machen.
Dazu habe ich eine header file clock.h.
in dieser habe ich auch ein array defininiert, in der die Anzahl der 
Tage für jeden Monat entahlten sind.
1
const uint8_t daysInMonth[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
diese header file wird in clock.c und in der main.c eingebunden.
Wenn ich das Programm jetzt compilieren möchte bekomme ich den error

'first defined here' in clock.o

Wie kann ich es am besten machen? Wenn ich diese variable in eine 
Globale header-file eintrage, wäre diese ja auch z.B. in uart sichtbar 
(es gibt eine uart.c und uart.h)

Jens

von g457 (Gast)


Lesenswert?

> Wie kann ich es am besten machen?

Deklaration in den Header, Definition in EIN c-File.

von Dirk B. (dirkb2)


Lesenswert?

Die Definition (mit der Initialisierung) vom Array gehört in clock.c
Die Deklaration (mit extern) gehört in die clock.h

von W.S. (Gast)


Lesenswert?

Jens schrieb:
> const uint8_t daysInMonth[12] = {31, 28, 31, ....
...
> Wie kann ich es am besten machen? Wenn ich diese variable in eine..

Also

Erstens: Du hast keine Variable, sondern ein Array von Konstanten 
deklariert. Das ist ein Unterschied. Variablen kommen in den RAM, 
Konstanten kommen in den Flash - normalerweise.

Zweitens: Bei Zahlenwerten von 28 bis 31 kannst du auf jedem System auch 
einfach ein simples char benutzen, denn das ist es ohnehin.

Drittens: In ein Headerfile kommt NICHT die Deklaration von irgend etwas 
hinein, was Platz im RAM oder Flash beansprucht. Sowas kommt IMMER in 
ein normales Quellfile (also hier ein xyz.c) hinein. Damit andere 
Programme davon etwas mitkriegen können, kommt in das zugehörige 
Headerfile eine quasi Kopie hinein, versehen mit dem Wort extern. Die 
anderen Programmteile müssen ja nur die Information haben, daß es da 
etwas Externes gibt und wie man drauf zugreifen kann.

also in clock.c
const char daysInMonth[12] = {31, 28, 31,...

und in clock.h
extern const char daysInMonth[12];

So, ist dir das jetzt klar?

W.S.

von STK500-Besitzer (Gast)


Lesenswert?

W.S. schrieb:
> Zweitens: Bei Zahlenwerten von 28 bis 31 kannst du auf jedem System auch
> einfach ein simples char benutzen, denn das ist es ohnehin.

Wobei bei manchen Systemen char ein int16 ist.

von Rolf M. (rmagnus)


Lesenswert?

W.S. schrieb:
> Erstens: Du hast keine Variable, sondern ein Array von Konstanten
> deklariert.

Selbstverständlich ist das eine Variable. Das const ändert nichts daran. 
Das Array auch nicht.

> Zweitens: Bei Zahlenwerten von 28 bis 31 kannst du auf jedem System auch
> einfach ein simples char benutzen, denn das ist es ohnehin.

char würde ich ausschließlich für Text benutzen. Wenn ich kleine 
Integers speichern will, immer explizit unsigned oder signed char. Das 
schützt vor Überrauschungen, denn für char ist in C nicht festgelegt, ob 
er signed oder unsigned ist.

> Drittens: In ein Headerfile kommt NICHT die Deklaration von irgend etwas
> hinein, was Platz im RAM oder Flash beansprucht.

Die Deklaration schon, nicht aber die Definition.

> Damit andere Programme davon etwas mitkriegen können, kommt in das
> zugehörige Headerfile eine quasi Kopie hinein, versehen mit dem Wort
> extern.

Das ist keine Kopie - weder quasi, noch unquasi. Es ist einfach eine 
Bekanntgabe des Namens und Typs - auch Deklaration genannt.

STK500-Besitzer schrieb:
> Wobei bei manchen Systemen char ein int16 ist.

In dem Fall gibt's aber auch keinen uint8_t. Dann böte sich 
uint_least8_t an - der kleinste Typ mit mindestens 8 Bit. Ein unsigned 
char ist aber das gleiche.

von Bauform B. (bauformb)


Lesenswert?

1
static const int  DAYS_IN_MONTH = 0b101011010101;
Februar ist sowieso ein Sonderfall und für normale Monate reicht ein 
Bit.

von Stefan F. (Gast)


Lesenswert?

Lies Dir mal diesen aktuellen Beitrag durch: 
Beitrag "Re: C++ Arduino Grundlagen"

Oder gleich die ganze Diskussion. Könnte hilfreich sein.

von Jürgen W. (Firma: MED-EL GmbH) (wissenwasserj)


Lesenswert?

Um Mehrfachdeklarationen zu vermeiden verwende ich immer folgendes 
Konstrukt (hier für CLOCK):


#ifndef CLOCK_H

#define CLOCK_H

// Header-Inhalt

#endif


Damit wird bei kaskadierten Aufrufen verhindert, daß der Header-Inhalt 
mehrfach eingebunden wird.

von Stefan F. (Gast)


Lesenswert?

Jürgen W. schrieb:
> Um Mehrfachdeklarationen zu vermeiden verwende ich immer ...

Das hatten wir schon. Es hilft dem TO nicht, da er diese header Datei in 
zwei C Dateien inkludiert, die einzeln compiliert werden. Wie gesagt 
erhält er dadurch zwei *.o Dateien, welche gleichnamige Symbole 
enthalten, und das ist unzulässig.

von Jürgen W. (Firma: MED-EL GmbH) (wissenwasserj)


Lesenswert?

Kurze Ergänzung (danke @ Stefanus, hab' das originäre Problem 
übersehen):

Ich nutze bei meinem MSP430-Arbeiten (viel Assembler) folgende Methode:

Im Source file (ob C oder ASM):
#ifndef [MODULNAME]_C
#define [MODULNAME]_C
#endif


In der Header-Datei:

#ifndef [MODULNAME]_H
#define [MODULNAME]_H

// Hier kommt der interessante Teil:
#ifndef [MODULNAME]_C
extern void funcName1(..);
extern unsigned short funcName2(...);  // entsprechend Funktionen
extern
#else
// Hier die internen Definitionen:
const uint8_t daysInMonth[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 
30, 31};

#endif  /* #ifndef [MODULNAME]_C */
#endif  /* #ifndef [MODULNAME]_H */



Der Vorteil dieser Variante ist, daß im Header alle globalen Variablen 
und auch Funktionen gemeinsam für alle anderen Source Files als extern 
definiert sind und man das nicht mehr per Hand ausführen muß.

von W.S. (Gast)


Lesenswert?

Jürgen W. schrieb:
> Im Source file (ob C oder ASM):
> #ifndef [MODULNAME]_C
...
> #define [MODULNAME]_C
> #endif

Ja, das macht man so aus Gründen der Sauberkeit, aber es ist im Prinzip 
nicht wirklich nötig, sofern man sich nicht angewöhnt hat, Includes auch 
in Headerdateien zu tun.

Sachlich soll in Headerdateien NICHTS stehen, was Speicherplatz kostet. 
Sondern es soll nur der passende Verweis stehen. Das ist der 
Unterschied.

Jürgen W. schrieb:
> #else
> // Hier die internen Definitionen:
> const uint8_t daysInMonth[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31,
> 30, 31};
>
> #endif  /* #ifndef [MODULNAME]_C */
> #endif  /* #ifndef [MODULNAME]_H */

Mache sowas lieber nicht, denn irgendwann schlägst du dir damit selbst 
die Beine weg. In deiner Headerdatei braucht eigentlich nur das zu 
stehen:
extern const uint8_t daysInMonth[];
und alles Andere gehört in die zugehörige .c hinein. Also weg mit dem 
ganzen #else Zeugs.

Also: Headerdateien sollen nur so viel enthalten, wie aus Sicht anderer 
Programmteile nötig ist.

W.S.

von Stefan F. (Gast)


Lesenswert?

Ich habe in ein großes Byte-Array im Quelltext, dass von einem Perl 
Script generiert wird. Weil das ein generiertes File ist, wollte ich das 
von meinem *.c Quelltext trennen und mit #include einbinden. Wie würdet 
ihr so eine Datei benennen? Ich habe sie *.inc genannt, ist das 
sinnvoll?

von Bauform B. (bauformb)


Lesenswert?

Ich nenne solche Dateien *.c und linke sie wie normalen Quelltext. Es 
steht zwar nichts ausführbares drin, aber es ist C-Quelltext. Den 
Unterschied sieht man an einer ersten Zeilen, z.B.
1
// DO NOT EDIT -- generated by ../tools/tzfile-to-c

von Bernd K. (prof7bit)


Lesenswert?

Du könntest zur Not ein static const im Header definieren, das ist dann 
in jeder Übersetzungseinheit einzeln vorhanden die den Header nutzt aber 
kollidiert nicht global (wegen static). Natürlich führt das zu vielen 
Instanzen der gleichen Konstante aber mit etwas Glück (gcc) erkennt der 
Linker das und entfernt die Redundanz.

: Bearbeitet durch User
von grundschüler (Gast)


Lesenswert?

1
//  Globals
2
#ifdef globals_main
3
#define ex 
4
#else
5
#define ex extern
6
#endif
7
ex volatile uint8_t txBuffer[buffer_len];
8
ex volatile uint8_t rxBuffer[buffer_len];


ich hab für sowas eine main.h. Die wird durch Definition von 
globals_main in der main.c auf Deklaration geschaltet - bei allen 
anderen Aufrufen ist es extern.

von Stefan F. (Gast)


Lesenswert?

Mit extern in einem Header und dann als normale *.c Datei compilieren 
gefällt mir. Dann habe ich eine Sonderlocke weniger. Danke euch.

von Bernd K. (prof7bit)


Lesenswert?

Normalerweise gibt es bestimmte best-practices die wenn man sie einhält 
einen davon befreien solche oder jegliche sonstige Verrenkungen 
anzustellen:

* Jeder Header hat einen include-guard
* Jeder Header inkludiert seinerseits genau all die header (und sonst 
keine) die er braucht um selbst für sich alleinstehend fehlerfrei 
geparsed werden zu können
* Obiges führt dazu daß die Reihenfolge der Inklusion beliebiger Header 
keinerlei Rolle mehr spielen kann! (Ja, das geht!)

* Globale Variablen werden im zugehörigen Header als extern deklariert, 
im zugehörigen Modul werden sie definiert.

* Jedes Modul bekommt seinen Header in dem nur Sachen drin stehen die 
dieses Modul exportiert.


* von Konstrukten wie #include "alle_includes.h" ist mit entschiedenem 
Nachdruck Abstand zu nehmen, solche Altlasten sind baldigst durch obiges 
wohlgeordnetes Vorgehen zu ersetzen.

: Bearbeitet durch User
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.