Schönen guten Tag Freunde der Elektronik!
Bei meinen bisherigen IDEs für Arm Cortex (Atmel Studio und IAR
Workbench) hatte ich im main.c File immer problemlos die anderen Files
wie folgt eingebunden:
#include "stdint.h" //nur benötigt wegen uintx_t Variablen
#include "own_def.h"
#include "global.h"
#include "interrupts.c"
#include "hw.c"
Allerdings stoße ich bei System Workbench for STM32 von AC6 (=Eclipse)
auf heftige Gegenwehr. So muß ich die Headerdateien in jedem .c File
vorstellen. Scheinbar werden die #includes nicht im main.c aufgelöst,
sprich deren Inhalte nicht aneinander gereiht, so daß die Funktionen der
Sourcefiles nicht die Definitionen der h.Files sehen. Hat das was mit
einem evtl. Einpassverhalten des internen Compilers zu tun?
Mit der zusätzlichen Einbindung der h-Files kann ich noch leben.
Allerdings kracht es dann bei den globalen Variablen. Diese in global.h
(enthält nur eine momentan) geht gar nicht. So meckert hw.c über die
angeblich nicht deklarierte Variable. Stelle ich diese in hw.c als
extern uint32_t errors vor, ergibt dies über 200 Fehler("first defined
here"), obwohl diese Variable nur an etwa 2 Stellen auftaucht. Ohne das
extern Attribut passiert das Gleiche. Was mache ich falsch, kann ich den
Compiler ggf. auf 2 Pass Verhalten umstellen?
Frustrierte Grüße,
Uwe
pegel schrieb:> Ich weiss nicht, ob ich das richtig verstanden habe, aber ändert sich> das Verhalten wenn du ein "Index->Rebuild" ausführst?
Hallo Pegel!
Es ändert sich leider nichts.
Dann wäre es vielleicht sinnvoll ein absolutes Minimal Beispiel zu
erstellen.
Das kannst du hier anhängen, dann könnte ich und auch andere das Problem
nachvollziehen.
Uwe M. schrieb:> #include "interrupts.c"> #include "hw.c"
Das lässt grundsätzliche Defizite bei den C-Grundlagen vermuten.
Da wundert es wenig, wenn es Fehlermeldungen nur so hagelt....
Sowas tut man nicht!
Der Unterscjied zwischen
Uwe M. schrieb:> #include "hw.c"
Wieso zum Teufel #includest Du eine *.c Datei? Wenn Du sie #includest,
musst Du sie .h nennen oder die IDE kommt furchtbar durcheinander.
Denn die IDE geht davon aus das alle *.c Dateinen einzeln compiliert
und dann später zusammem verlinkt werden.
Der Indexer jagt die *.c Dateien einzeln duch den Compiler.
Man includiert keine *.c Dateien und man deklariert keine Variablen in
*.h Dateien.
Informiere Dich mal über den Sinn von Header Dateien.
Wenn du eine globale Variable in allen *.c Dateien verfügbar machen
willst, musst du das Schlüsselwort extern benutzen. Aber das wäre
ziemlich schlechter Stil.
Sauber wäre, die Variable in einer *.c Datei zu haben, zusammen mit
getter- und setter- Funktionen. Diese wiederum werden über eine
gleichnamige *.h Datei den anderen Quelltexten bekannt gemacht.
Stefanus F. schrieb:> Sauber wäre, die Variable in einer *.c Datei zu haben, zusammen mit> getter- und setter- Funktionen. Diese wiederum werden über eine> gleichnamige *.h Datei den anderen Quelltexten bekannt gemacht.
Da verwechselst du wohl C und C++.
In C ist es durchaus üblich (und wird auch so gelehrt), globale
variablen in einer Header-Datei als extern zu deklarieren.
Daran ist nichts "unsauber".
Harry L. schrieb:> Der Unterscjied zwischen#include "foo.h"> und#include <foo.h>> scheint dir auch nicht ganz klar zu sein.
Da ich ALLE Dateien in einem Arbeitsverzeichnis habe, sind meine
#includes "xyz" korrekt. Was gibt es da zu bemängeln? Allerdings nehme
ich den Vorwurf mit dem Inkludieren der c-files sehr ernst, danke!
Und in der Tat kannte ich die Indexer Funkion bisher überhaupt nicht, da
ich nicht sonderlich gut die Internas von Compilertechniken kenne. Ich
mache mich da mal fit, klingt interessant, danke!
Der Compiler wird das Main.c Chaos mit den .c includes fehlerfrei
übersetzen, solange keine Symbole mehrfach vorkommen ist das erstmal von
der Syntax ok. Include fügt die angegebene Datei nur an diese Stelle
ein.
Das CDT von Eclipse das das Projekt scannt und makefiles erzeugt findet
aber auch die anderen C Quellen und übersetzt die auch. Wenn die
fehlerfrei waren wird der Linker jetzt aber meckern weil Symbole doppelt
vorhanden sind, einmal im Main.o und dann in denn jeweiligen anderen
Modulen.
Uwe M. schrieb:> Da ich ALLE Dateien in einem Arbeitsverzeichnis habe, sind meine> #includes "xyz" korrekt. Was gibt es da zu bemängeln?
stdint.h ist ein system-weit verfügbarer Header, und muß daher mit
1
#include<stdint.h> //nur benötigt wegen uintx_t Variablen
referenziert werden weil der Pfad zwar in der Liste der Include-Pfade
des Compiler steht (stehen sollte) aber ansonsten je nach
Entwicklungsumgebung- variabel ist.
Offensichtlich hast du das ja noch nicht ganz vollständig verstanden.
Bevor man mich verbal (weiter) zerreißt, hier noch die Intention meines
klar experimentellen Codes. Diesen habe ich übrigens mittlerweile nach
den klassischen Regeln kompiliert bekommen mit einiger Schreibarbeit.
Und da sind wir nun bei meiner ursprünglichen Intention: Wenn man alles
in einer Datei programmieren würde, könnte man sich einiges an
Schreibarbeit ersparen: Prototypen von Funktionen, Verweis auf externe
Variablen. Natürlich wäre so eine einzige Datei extrem lang,
unübersichtlich und daher chaotisch, weiß ich, habe vor zuletzt 16
Jahren selber codiert. Und da dachte ich mir rein experimentell, ich
lagere aus dieser langen Datei (im Anfangsstadium) strukturiert aus,
Treiberteil in hw.c, Interrupthandler in interrupts.c, aber diese Teile
müssen zum Zeitpunkt der Compilierung wieder zu einem Teil (der
ursprünglichen großen Datei) zusammenfinden mit Hilfe von includes, für
den Programmierer natürlich unsichtbar. Aber leider scheint es paar
Nebeneffekte zu geben, die mir wegen fehlendem tiefen
Compilerkenntnissen verborgen blieben. Schade, aber im Ansatz eine nette
Idee.
Gruß und vielen Dank,
Uwe
Harry L. schrieb:> In C ist es durchaus üblich (und wird auch so gelehrt), globale> variablen in einer Header-Datei als extern zu deklarieren.> Daran ist nichts "unsauber".
Nur weil es "ueblich" ist, ist es noch lange nicht gut. Und es ist
unsauber. Denn wenn du die Variable mal umbenennen musst, dann musst du
viele Dateien anfassen. Mit einer sauberen Schnittstelle durch
Funktionen muss nur eine einzige Datei angefasst werden. Auch das
Debuggen ist deutlich aufwaendiger. Gestattet man den Zugriff nur ueber
Funktionen, habe ich max. 2 Stellen an denen die Variable veraendert
wird und an denen ich einen Breakpoint setzen muss: in der set- und in
der get-Funktion.
Machst du eine globale Variable ueber extern bekannt, hast du halt kein
sauberes API zwischen den Code-Modulen... aber wer braucht das schon. Da
trennen sich halt die echten Maenner von den Muesliessern...
Uwe M. schrieb:> Und da sind wir nun bei meiner ursprünglichen Intention: Wenn man alles> in einer Datei programmieren würde,
Dazu fehlt mir nur ein: 'kann man so machen, dann ist es halt Kacke'.
Das es nicht geht liegt nicht am Compiler sondern wie beschrieben am
Automatismus des CDT Builders. Der sucht alle Projektverszeichnisse nach
.c/.cpp Dateien ab und fügt die in makefiles ein. Damit werden deine .c
doppelt compiliert, einmal als include im main und einmal einzeln. Und
dabei fehlen dir die Includes in interrupt.c weil du die nur vorher im
main angegeben hast. Aber was in main.c steht das interessiert
interrupt.c nicht die Bohne.
Es wird funktionieren wenn du alle .c files ausser main.c vom Build
ausschliesst. Das geht in Eclipse: Rechtsklick auf die .c Datei,
Resource Configurations / Exclude from Build... und da alle auswählen.
Aber wie gesagt, das ist gegen Modularisierung und verhindert
inkrementelle builds (nur das übersetzen was sich geändert hat). Ich
würde es nicht so machen. Das ist genauso blöd wie der Arduino .ino
Mist, nur weil man den Leuten nicht zumuten möchte das
Implementierung/Interface Konzept verstehen zu sollen.
Kaj schrieb:> Denn wenn du die Variable mal umbenennen musst, dann musst du> viele Dateien anfassen.
Wenn das der einzige Grund ist: heute gibt es IDEs die refactorieren.
Ein Klick und die Variable ist überall unbenannt. Und jeder Profi
benutzt eine IDE und kein Editor-Makefile gewurstel.
Kaj schrieb:> Mit einer sauberen Schnittstelle durch> Funktionen muss nur eine einzige Datei angefasst werden
Klingt sicherlich gut, optisches Software Engineering vom Feinsten, aber
über den dramatischen Geschwindigkeitsverlust beim Lesen und Beschreiben
dieser Variablen, geschätzt etwa etwa Faktor 5, schweigst du dich
natürlich aus.... Fazit: Es gibt keine tolle Lösung für globale
Variablen!
Kaj schrieb:> enn wenn du die Variable mal umbenennen musst, dann musst du> viele Dateien anfassen
Oder vorher mal einen sinnvollen bzw. den gewünschten Namen verwenden.
Da die meisten mit einer IDE programmieren dürften, sollte es damit auch
per Suchen/Ersetzen problemlos möglich sein.
Uwe M. schrieb:> Diesen habe ich übrigens mittlerweile nach> den klassischen Regeln kompiliert bekommen mit einiger Schreibarbeit.
Schon mal was von der Zwischenablage gehört?
Die Geschichte mit dem extern in der Header-Datei hat auch was von
Datenkapselung: Variablen, die nur in der C-Datei auftauchen, können
auch nur dort von diesem "Modul" verwendet werden.
Gibt man sie mir extern (sieht hübsch aus, ist aber mWn nicht mehr
nötig) in der Header-Datei an, können auch andere Module schreibend
darauf zugreifen.
Das umgeht Kay durch die Verwendung von Funktionen (die in der
Header-Datei dann aufgeführt werden müssen, damit andere Module darauf
zugreifen können).
Kaj schrieb:> Gestattet man den Zugriff nur ueber> Funktionen, habe ich max. 2 Stellen an denen die Variable veraendert> wird und an denen ich einen Breakpoint setzen muss:> in der set- und in der get-Funktion.
Schweifen wir jetzt in die objektorientierte Programmierung ab?
Stefanus F. schrieb:> Wenn du eine globale Variable in allen *.c Dateien verfügbar machen> willst, musst du das Schlüsselwort extern benutzen. Aber das wäre> ziemlich schlechter Stil.
Ähem..
ich mache das mal konkret:
in blabla.c schreibt man beispielsweise
int MeineVariable;
und im zugehörigen blabla.h schreibt man
extern int MeineVariable;
und wie schon geschrieben wurde, inkludiert man NICHT die .c Datei,
sondern nur die zugehörige .h Datei in anderen Programm-Moduln.
Man kann das seit geraumer Zeit so machen, daß man alles was man an
Variablen und Funktionen in die .h schreibt, mit extern versieht. Auch
Funktionen. Das übersieht zwar der Compiler, aber es ergibt sich damit
eine vereinheitlichte Darstellung in der Headerdatei, was zumindest ich
als positiv empfinde, weil damit eine Extrawurst für Funktionen
beseitigt ist.
W.S.