Hallo zusammen
In meinem Header-File definiere ich ein konstantes Array, welches
Komandoeigenschaften beinhaltet "cyfx3fw.h":
1
#ifndef _INCLUDED_CYFX3FW_H_
2
#define _INCLUDED_CYFX3FW_H_
3
4
...
5
6
typedefstructStm32Cmd_t
7
{
8
CyBool_tCmdVald;
9
CyBool_tLongRun;
10
uint8_tReqDCnt;
11
uint8_tRspDCnt;
12
}Stm32Cmd_t;
13
14
constStm32Cmd_tStm32Cmd[256]={{.CmdVald=CyFalse,
15
.LongRun=CyFalse,
16
.ReqDCnt=0x00,
17
.RspDCnt=0x00},
18
[0x80]={.CmdVald=CyFalse,
19
.LongRun=CyFalse,
20
.ReqDCnt=0x00,
21
.RspDCnt=0x00}};
22
23
...
24
25
#endif /* _INCLUDED_CYFX3FW_H_ */
Beim Build kriege ich folgende Fehlermeldung:
1
./cyfx3fwdscr.o:(.rodata.Stm32Cmd+0x0): multiple definition of `Stm32Cmd'
2
./cyfx3fw.o:(.rodata.Stm32Cmd+0x0): first defined here
3
collect2: ld returned 1 exit status
4
cs-make: *** [Fx3Fw.elf] Error 1
"cyfx3fw.h" steht sowohl inder include-List von "cyfx3fw.c" als auch von
"cyfx3fwdscr.c", sollte aber wegen des "_INCLUDED_CYFX3FW_H_"
kein Problem sein...
.. wo stehe ich hier auf dem Schlauch?
P. K. schrieb:> "cyfx3fw.h" steht sowohl inder include-List von "cyfx3fw.c" als auch von> "cyfx3fwdscr.c", sollte aber wegen des "_INCLUDED_CYFX3FW_H_"> kein Problem sein...
Doch. Wie du richtig festgestellt hast, taucht das Array dann in beiden
.c Dateien auf, somit in beiden .o Dateien und wäre somit doppelt in der
Ausgabedatei. Du musst das Array nur in genau einer .c Datei definieren.
Wenn du von anderen .c Dateien drauf zugreifen willst, musst du es in
denen (oder im Header) mit "extern" und ohne Initialisierung
deklarieren.
P. K. schrieb:> .. wo stehe ich hier auf dem Schlauch?
die Variabel muss ein einer C Datei zu hause sein. In der Header Datei
muss ein extern drin sein.
Du legst ja in der Header-Datei eine Variabel an, mit wird sie in jeder
Übersetzungseinheit auch neu eingebunden.
Hi,
normaleweise sollte man die definition in ein C-Source packen, und nicht
in den Header. In den header kann dann die "extern" deklaration machen.
Gruß,
Bernhard
P. K. schrieb:> "cyfx3fw.h" steht sowohl inder include-List von "cyfx3fw.c" als auch von> "cyfx3fwdscr.c", sollte aber wegen des "_INCLUDED_CYFX3FW_H_"> kein Problem sein...>> .. wo stehe ich hier auf dem Schlauch?
Um da noch nachzuhaken:
Der springende Punkt ist, dass jede C-Datei für sich OHNE ANSEHEN VON
ANDEREN C DATEIEN übersetzt wird.
Dein Include Guard, der dich davor schützt, das dieselben Definitionen
mehrfach gesehen werden, wenn EINE C-Datei übersetzt wird (weil zb
derselbe #include über mehrere ineinander geschachtelte #include
auftereten kann), kann dir daher nicht helfen, wenn in 2 C-Dateien
derselbe #include auftaucht.
Jedes C-File ist für sich unabhängig von allen anderen. Oder anders
gesagt: Wenn der Compiler cyfx3fw.c compiliert, dann interessiert ihn
nicht welche #include bzw. welche #define vorgenommen wurden als
cyfx3fwdscr.c compiliert wurde. Sobald er sich cyfx3fw.c vornimmt,
beginnt alles wieder bei 0. Und natürlich vice verse, wenn cyfx3fwdscr.c
compiliert wird.
Mein Ziel war es, alle Defines und beschreibenden Konstanten welche das
System konfigurieren (und gelegentlich angepasst warden müssen) an einem
Ort zu haben (minimiert das Suchen, wenn man nach zwei Jahren was
kleines ändern will).
1
staticconstStm32Cmd_tStm32Cmd[256]={...
löst für mich das Problem soweit. Irgendwelche Drawbacks?
>Irgendwelche Drawbacks?
Das müsste man mal im Kontext sehen. Falls Du nun zwei Dateien mit
includes hast, wie oben beschrieben, die jeweils "static const
Stm32Cmd_t ..." enthalten, dann hast Du auch tatsächlich (d.h. physisch)
zwei gleichnamige Variablen, die jeweils nur von dem Modul zugegriffen
werden, in denen sie erscheinen. Du hast aber nicht eine Variable
deren Inhalt von zwei verschiedenen Modulen aus zugegriffen werden. Das
aber war, nach Deinem ersten Posting Deine Absicht.
Tschuldigung, mir ist das include entschlüpft. Es muss natürlich sein:
Vielleicht kann ein mod meinen post oben editieren und diesen hier
löschen.
Die H-Datei:
EXTERN int a;
Erste C-Datei:
// mit dem define wird eine Deklaration erzeugt
#define EXTERN extern
#include "hdatei.h"
int function1 (...)
{
a = ...;
}
Zweite C-Datei:
// mit dem define wird eine Definition erzeugt
#define EXTERN
#include "hdatei.h"
int function2 (...)
{
a = ...;
}
Bitflüsterer schrieb:> Tschuldigung, mir ist das include entschlüpft. Es muss natürlich sein:
dann hast du die Datei auch mehrfach. In jeder Übersetzungseinheit ist
dein define wieder weg.
Bitflüsterer schrieb:>>In jeder Übersetzungseinheit ist dein define wieder weg.>> Das soll es ja auch. Schauen wir uns mal die Expansion nach dem include> an.
das ist ja umständlich und fehleranfällig.
warum nicht ein Header und eine C Datei nehmen wie es üblich ist?
>das ist ja umständlich und fehleranfällig.
Das kann man so oder so sehen. Was der eine für umständlich hält kann
der andere durchaus handhaben. Ich habe allerdings auch vorausgeschickt,
das es andere Methoden gibt. Das Grundproblem ist allerdings, das man
immer getrennte Übersetzungseinheiten hat. Also muss in jeder
Übersetzungseinheit ein Unterschied vorhanden sein, der kontrolliert, ob
eine Definition oder eine Deklaration erzeugt wird.
>warum nicht ein Header und eine C Datei nehmen wie es üblich ist?
Es ist genau eine Header-Datei.
Es sind zwei C-Dateien weil es eben zwei C-Dateien sind. Ich weiss
garnicht, was ich darauf sagen soll, als das es üblich ist, grössere
Projekte in mehrere C-Dateien zu unterteilen. So hat es der TO ja auch
beabsichtigt, so mache ich es auch und so machen es viele Andere.
Bitflüsterer schrieb:>>das ist ja umständlich und fehleranfällig.> Das kann man so oder so sehen. Was der eine für umständlich hält kann> der andere durchaus handhaben. Ich habe allerdings auch vorausgeschickt,> das es andere Methoden gibt. Das Grundproblem ist allerdings, das man> immer getrennte Übersetzungseinheiten hat. Also muss in jeder> Übersetzungseinheit ein Unterschied vorhanden sein, der kontrolliert, ob> eine Definition oder eine Deklaration erzeugt wird.
nicht für die normalen C Dateien. Es gibt es noch ein 3. C Datei und
dort steht die variable drin.
>>warum nicht ein Header und eine C Datei nehmen wie es üblich ist?>> Es ist genau eine Header-Datei.
gut, aber keine C Datei dazu.
> Es sind zwei C-Dateien weil es eben zwei C-Dateien sind. Ich weiss> garnicht, was ich darauf sagen soll, als das es üblich ist, grössere> Projekte in mehrere C-Dateien zu unterteilen. So hat es der TO ja auch> beabsichtigt, so mache ich es auch und so machen es viele Andere.
ist ja ok, dann leg einfach noch eine 3. Datei an und leg die Variable
dort ab. Schon können die anderen C Dateien gleich sein.
Auf meine Konstante wird nur in einem .c-File zugegriffen.
Ich gehe davon aus, dass in den anderen .c-Files (welche zwar #defines
aus dem .h-file kennen, aber die Konstante nicht benutzen) der Compiler
die (unnötige) Konstante wieder entsorgt (?).
Falls nicht: die 512 Bytes und 512 Bools sind nun ja nicht so ein
Riesenposten, die Bequemlichkeit, alle Config-Daten (Konstanten &
Defines) in einem File zu haben, würde den Overhead mehr als
rechtfertigen.
P. K. schrieb:> Falls nicht: die 512 Bytes und 512 Bools sind nun ja nicht so ein> Riesenposten, die Bequemlichkeit, alle Config-Daten (Konstanten &> Defines) in einem File zu haben, würde den Overhead mehr als> rechtfertigen.
Wenn Du die "Konstanten & Defines" in eine eigene c-Datei auslagerst
und einen header mit "extern" machst, hast Du auf saubere Weise genau
das selbe ohne n*1kByte zu verschwenden.
>nicht für die normalen C Dateien. Es gibt es noch ein 3. C Datei und>dort steht die variable drin.
Das kann man machen. Hat aber auch Nachteile. Beide Methoden haben Vor-
und Nachteile. Denn dann hast Du eine H-Datei mit den Typen und
extern-Verweisen und eine C-Datei mit den Typen. Willst Du den Typ
ändern, dann musst Du zwei Dateien editieren. Die C-Datei mit den
Definitionen und die H-Datei mit den Deklarationen.
-------------
Eine andere Methode ist die Verwendung von "Zugriffsfunktionen".
h-Datei:
Bitflüsterer schrieb:>>das ist ja umständlich und fehleranfällig.> Das kann man so oder so sehen. Was der eine für umständlich hält kann> der andere durchaus handhaben.
Ich hab das vor Jahren auch eine Zeitlang so gemacht, dass ich ein
EXTERN per Bedarf in einem C-File wegdefiniert habe. Das funktioniert
auch ganz gut.
Aber
Das Problem taucht genau dann auf, wenn du nicht nur Definition und
Deklaration in einem einzigen Header File haben willst, sondern wenn
dann auch noch Initialisierungen ins Spiel kommen. Spätestens dann wirds
nämlich aufwändig und unübersichtlich.
Das Problem: Bei
1
externinta=5;
gewinnt die Initialisierung. Das ist eine Definition und keine
Deklaration. Womit der ganze Hack rund um
Header
1
#ifndef EXTERN
2
#define EXTERN extern
3
#endif
4
5
EXTERNinta=5;
File1.C
1
#include"Header"
2
3
...
File2.c
1
#define EXTERN
2
#include"Header"
3
4
intmain()
5
{
6
...
7
}
ins Wasser fällt. Klar kann man mit zusätzlicher Makro-Magie die
Initialisierung wieder soweit verpacken, dass man sie gekoppelt an das
EXTERN Makro erst per Bedarf nur an einer einzigen Stelle bei der
Definition 'aktiviert', aber der Aufwand wird dann schon so hoch und das
ganze wird so unübersichtlich, dass es sich nicht mehr lohnt.
P. K. schrieb:> Auf meine Konstante wird nur in einem .c-File zugegriffen.
Dann erhebt sich allerdings die Frage, warum diese Konstanten nicht
überhaupt in genau diesem .c-File sind und nirgendwo anders.
Header Files haben den Sinn, die Dinge zu kapseln, die ein
Aussenstehender von einem 'Modul' (= dieses C-File) wissen muss. Wenn
ein Aussenstehender von diesen Konstanten nichts wissen muss oder wissen
soll, dann haben die auch nichts im Header File verloren.
> Falls nicht: die 512 Bytes und 512 Bools sind nun ja nicht so ein> Riesenposten, die Bequemlichkeit, alle Config-Daten> (Konstanten & Defines) in einem File zu haben, würde den Overhead> mehr als rechtfertigen.
Aber nicht um jeden Preis.
Leg um die Struktur bzw. die Initialisierung wenigstens noch ein #ifdef
drumm herum, so dass sich dieses eine C-File diese Definitionen
'freischalten' kann, und in allen anderen C-Files diese Dinge vom
Präprozessor gleich mal rausgeworfen werden.
Config.h
1
....
2
3
#ifdef NEED_COMMANDS // wird nur von File Commands.c benoetigt
@ Karl Heinz
Ich stimme Dir im Grundsatz zu, wobei allerdings auch das mit einfachen
Mitteln zu ändern ist, die ich, im Gegensatz zu Dir, nicht als "Magie"
bezeichnen würde.
Wie schon geschrieben: Verschiedene Lösungen haben verschiedene Vor- und
Nachteile.
Bei der anderen mit einer C-Datei und einer H-Datei kommt man aber man
ohne "Magie" :-) auch nicht aus, denn man müsste die enthaltenen EXTERNs
irgendwie deaktivieren, wenn das H-File in der C-Datei mit den
Definitionen verwendet wird. Die andere Lösung würde voraussetzen das
man an zwei Stellen Typen nennt, die, falls sie geändert werden sollen
eben auch an zwei Stellen geändert werden müssen.
Wie gesagt: Eine Kröte muss man schlucken.
@ Karl Heinz
>#define NEED_COMMANDS
Genau. Aber das ist dann der nächste gedankliche Schritt.
Für mich war das primäre Problem, erstmal eine Lösung zu beschreiben.
Das die weitere Umstände erfordert war für mich eine weiteres Thema. Ich
habe bei solchen Threads das Problem, das ich nicht weiss wie
fortgeschritten der TO ist. Wenn einer solch eine Frage stellt, dann
schreibe ich erstmal die einfache Lösung, die weiterentwickelt wird.
Ich sagte ja: Es gibt andere Lösungen. Aber die sind gedanklich etwas
komplizierter. Bei der Frage ob ich die hier beschreibe, habe ich mich
eben so entschieden.
Bitflüsterer schrieb:> Bei der anderen mit einer C-Datei und einer H-Datei kommt man aber man> ohne "Magie" :-) auch nicht aus, denn man müsste die enthaltenen EXTERNs> irgendwie deaktivieren, wenn das H-File in der C-Datei mit den> Definitionen verwendet wird.
Da dürfte jetzt ein Missverständnis vorliegen
globals.h
1
externinta;
globals.c
1
#include"globals.h"
2
inta=5;
main.c
1
#include"globals.h"
2
3
intmain()
4
{
5
printf("%d",a);
6
}
keinerlei Makro-Magie notwendig. In globals.c stehen die Definitionen
der globalen Variablen, die in globals.h deklariert wurden.
(bzw. dann sinngemäss eben immer adaptiert auf C-File + zugehöriges
Header File)
Zu Fehlern kann es nicht kommen, denn das globals.c inkludiert das
Header-File ebenfalls, so dass der Compiler die Übereinstimmung in den
Datentypen kontrollieren kann.
Du kannst jetzt natürlich anführen, dass man dann ja wohl bei der
Einführung von zusätzlichen globalen Variablen an jeweils 2 Stellen
editieren muss und du hast recht damit. Allerdings ist das aber auch
keine Operation, die man jeden Tag 300 mal macht. Von daher sind die 30
Sekunden zusätzliche Editieroperation alle 3 Wochen verschmerzbar und
kein echtes Problem.
@ Karl Heinz
Da gebe ich Dir recht. K&R 2. Aufl. A.10.2., S. 224
Aber:
>Du kannst jetzt natürlich anführen, dass man ...
was ich dann jetzt hier mal anführe. :-)
Unschön ist das aber auch, wenn ich die Initialisierung so ändere das
ein anderer Typ nötig ist. Dann muss ich auch an zwei Stellen ändern.
Es gab da vor einigen zig Jahren bei mir mal so einen Fall, in dem ich
einige Dutzend Initialisierungen und Typen ändern musste. Irgendwie ist
das hängengeblieben. Aber gut, das ist ein Sonderfall.
Bitflüsterer schrieb:> Unschön ist das aber auch, wenn ich die Initialisierung so ändere das> ein anderer Typ nötig ist. Dann muss ich auch an zwei Stellen ändern.
Nicht ganz elegant, gebe ich zu.
Aber solange es nicht passieren kann, dass etwaige Fehler an dieser
Stelle unentdeckt bleiben, ist es ein Schönheitsproblem.
Wichtig ist immer, das der Compiler die Dinge kontrollieren kann. Also
... für mich ist das immer das wichtigste und hat oberste Priorität.
Denn ich mache Fehler und da bin ich schon ganz froh drüber, wenn mir
der Compiler im Falle des Falles auf die Finger klopft. Ein bischen
Tippaufwand hingegen stört mich weniger.
@ Karl Heinz
> ... dass etwaige Fehler an dieser Stelle unentdeckt bleiben, ...
Das ist nun wieder ein Argument, dass auf seinen drei Beinen auch recht
sicher steht. :-)
Als gut. Ich hoffe der TO hat was von dieser Diskussion gehabt.
Bitflüsterer schrieb:> Als gut. Ich hoffe der TO hat was von dieser Diskussion gehabt.
Ja, merci. Gibt einen schönen Überblick. Werde mir noch ein paar
Gedanken machen, wie ichs dann wirklich implementiere (momentan
quick&dirty mit dem Speicherverbraucher "static"...).
Wie gesagt: Ursprüngliches Ziel war, alles was vom System gegeben ist
(e.g. STM32 command Struktur, PC command Struktur, etc.) für den FX3 in
einem_ _einzigen File zu definieren, um später, wenn aussen was
ändert, nicht suchen zu müssen.
Noch eleganter (um alles an einem Ort und nichts doppelt zu haben) wären
natürlich gemeinsame zentrale Definitionsstrukturen für alle Beteiligten
(PC, FX3, FPGA, eMMC und STM32) aber das ist mir im Moment doch zu etwas
zu haarig...