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.
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
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.
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.
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.
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.
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.
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ß.
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.
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?
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
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.
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.
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.