Hallo,
Ich habe schon mehrere Programme für den Arduino Uno geschrieben. Sie
funktionieren zwar aber bezogen auf den Deklarationsort der Variablen
weiß ich nicht, warum. Es ist bei mir Try und Error ohne wirkliches
System. Mir ist einfach nicht klar, wo in einem Arduino Uno Programm die
Variablen deklariert werden sollen. Ich finde Programme mit einer
Deklaration an verschiedenen Stellen:
1) vor dem Setup
2) im Setup
3) im Loop
Manchmal sind die Variablen gleich am Anfang des gesamten Programmtextes
deklariert. Ein anderes Mal werden sie erst in dem Moment deklariert,
wenn sie benötigt werden, z.B. "i" in folgendem Beispiel:
1
for(inti=0;i<10;i++)
Ich finde nur Erklärungen, die auf Programme eingehen, die
Unterprogramme beinhalten. Ich sehe aber schon ein Problem in einem
Programm ohne Unterprogramme. Gehen wir also im Folgenden davon aus,
dass es keine Unterprogramme gibt.
Kann man dann jede Variable an jeder Stelle eines Programmes
deklarieren, wenn das nur vor Ihrer Benutzung geschieht?
Hat es unterschiedliche Auswirkungen, wenn die Deklaration an
verschiedenen oben genannten Stellen passiert?
Wo finde ich Infos dazu, wo in Arduino Programmen ohne Unterprogramm
Variablen sinnvoll deklariert werden?
Es gelten die C++ üblichen Regeln.
Suchtipp: C++ Geltungsbereich oder Gültigkeitsbereich
Mantra:
Eine vermeidbare globale Variable ist ein böse Variable.
Eine vermiedene globale Variable ist ein gute Variable.
Servus,
also im allgemeinen ist programmieren geschmackssache, da hat jeder
seine eigene Note dabei. Wichtig ist dass es funktioniert und
verständlich ist.
Ich für meinen Teil deklariere meine Variablen IMMER am Anfang also vor
dem Setup. Im Setup selbst setze ich dann meine I/Os etc.
Im loop selbst habe ich dann nur das eigentliche mainprogramm stehen.
Ernie B. schrieb:> Manchmal sind die Variablen gleich am Anfang des gesamten Programmtextes> deklariert. Ein anderes Mal werden sie erst in dem Moment deklariert,> wenn sie benötigt werden, z.B. "i" in folgendem Beispiel:for(int i=0;> i<10; i++)
Das kommt einfach aus dem Grund, dass i hier eine klassische
zählvariable ist und nur als Schleifenbedingung dient. Weder vorher,
noch nachher ist die variable relevant und wird (soweit ich weiß) nach
verlassen der Schleife auch wieder gelöscht.
Es kommt immer drauf an, wofür die Variablen verwendet werden und
wer/was darauf zugreifen soll.
Mal so eben schnell:
Mit am Anfang des Codes:
Das sind globale Variablen. An jeder Stelle und in jeder Funktion kann
darauf zugegriffen werden. Wenn die Variablen in Interrupt verändert
werden sollen, ist es wichtig, sie als "volatile" zu deklarieren.
Im Setup:
Damit kann man auch nur im Setup und in der loop-Schleife drauf
zugreifen. Funktionen und Interrups wissen davon nichts.
In der loop:
Dort kann die Variable nur in der Loop-Schleife verwendet werden. Wie im
Setup.
In einzelnen Schleifen (for/while/...):
Die Variable kann nur innerhalb der Schleife verwendet werden. Danach
wird der Speicherplatz wieder freigegeben und für anderes verwendet
In Funktionen:
Auch hier kann die Variable nur innerhalb der Funktion verwendet werden.
Beim Verlassen der Funktion wird der Speicher freigegeben und kann von
was anderem verwendet werden.
Wenn man eine Variable nur kurz zum Zählen braucht, wäre es also
Platzveschwendung, sie als globale Variable anzulegen und die Daten
ständig vorzuhalten. Das gleiche gilt für Funktionen, in denen die
Variable vllt. nur für Zwischnrechnungen gebraucht wird.
Mit bestimmten Keywords bei der Deklaration kann beeinflusst werden, was
mit der Variable passiert. Z.B. "volatile" (s.o.) und "static".
In Funktionen kann z.B. "static" nützlich sein. Der Inhalt der Variable
bleibt damit auch beim Verlassen der Funktion erhalten und beim nächsten
Aufruf der Funktion wird sie nicht neu deklariert/initialisiert.
Ernie B. schrieb:> Gehen wir also im Folgenden davon aus,> dass es keine Unterprogramme gibt.
falsche Annahme!
weil
Ernie B. schrieb:> Arduino
Es gibt mindestens die Unterprogramme setup & loop.
Auch in pure C AVR o.ä. gibt es die main und die while(1) -> loop
In einer Funktion {} ist eine Variable lokal und wird nach Verlassen
verworfen
Ernie B. schrieb:> for(int i=0; i<10; i++)
->(Ausnahme static), sie ist dann nicht mehr existent oder unverändert.
Ausserhalb einer Funktion {} ist eine Variable global und wird nach
Verlassen nicht verworfen, sie existiert noch mit dem ihrem letzten
Wert.
Ernie B. schrieb:> Wo finde ich Infos dazu, wo in Arduino Programmen ohne Unterprogramm> Variablen sinnvoll deklariert werden?https://de.wikipedia.org/wiki/Static_(Schl%C3%BCsselwort)
Sebastian R. schrieb:> Im Setup:> Damit kann man auch nur im Setup und in der loop-Schleife drauf> zugreifen. Funktionen und Interrups wissen davon nichts.
Nein.
Eine in setup() definierte Variable ist nicht in loop() sichtbar.
Sie verfällt am Ende von setup()
Sebastian R. schrieb:> In Funktionen kann z.B. "static" nützlich sein.
Naja...
Kann.
Sollten aber vermieden werden, da diese Funkion dann überraschende
Seiteneffekte zeigen kann.
Das gilt auch für die Verwendung von globalen Variablen in Funktionen.
Arduino Fanboy D. schrieb:> Sollten aber vermieden werden, da diese Funkion dann überraschende> Seiteneffekte zeigen kann.
denkst du an IRQ volatile oder 16-Bit Zugriffe ohne atomic?
Arduino Fanboy D. schrieb:> Eine in setup() definierte Variable ist nicht in loop() sichtbar.> Sie verfällt am Ende von setup()
Ah, okay. Da es im Prinzip eine eigene Funktion ist, klingt es logisch.
Ich hatte zu sehr an normales C gedacht und die Variablen am Anfang von
main() und vor der Hauptschleife darin angesiedelt. Da wäre es dann so
gewesen.
Joachim B. schrieb:> denkst du an IRQ volatile oder 16-Bit Zugriffe ohne atomic?
Nein!
Mir stellen sich immer die Nackenhäärchen auf, wenn ich Funktionen sehe,
welche sich, je nach vorherigem Aufruf, auf Grund eines inneren
Zustands, anders verhalten.
Ich finde eine Funktion ist leichter zu debuggen,
einzusetzen/wiederverwenden und zu begreifen, wenn sie IMMER das gleiche
tut, wenn sie die gleichen Parameter erhält.
Funktionen sollten keinen eigenen inneren Zustand mit führen.
Das entspricht dem Prinzip der geringsten Verwunderung.
Es gibt Sprachmittel, mit welchen mal sowas schöner abhandeln kann.
Z.B. globale Strukturen übergeben. (C)
Oder OOP, da weiß man, dass eine Instanz einen inneren Zustand hat(haben
kann), und es ist dennoch mehrfach instanziierbar. (C++)
Hallo
gut erklärt und eigentlich ganz einfach.
Nur wie ich selbst erfahren musste - in der Praxis und
Programmverständnis dann eben doch nicht(nicht zuletzt wegen der
beeinflussenden Keywords).
Eigentlich recht ähnlich zur höheren Schulmathematik wo alles für sich
und isoliert auch ganz einfach ist, aber dann in der Praxis und im
Zusammenhang... das kennt wohl jeder noch aus der Schulzeit falls er
diese nicht vollständig verdrängt hat (Die nämlich niemals so schön war
wie man es sich so gerne einredet sondern oft eher das Gegenteil...)
Es braucht seine Zeit bis man es -wirklich- versteht und nachvollziehen
kann - und fast noch wichtiger:
Verständliche und sich auf einen "echten" Programm beziehende
Erklärungen über das wie und vor allem warum.
Letztendlich sinnarme Programme nur der (Nicht-)erklärung wegen bringen
leider sehr wenig, noch weniger bringt einen Lernenden irgendwelche,oft
sogar kryptische, Einzelerklärungen oder "Funktionsbeschreibungen" von
Befehlen und Keywords.
Das sind mehr Erinnerungen und Nachschlagewerke für die die so was
eigentlich nicht mehr brauchen oder wenn es um ganz gemeine Fallen und
Details geht.
Programmieren gut und verständlich zu vermitteln ist eine Kunst die kaum
einer wirklich beherrscht.
Als jemand der Programmieren, und sei es "nur" einen Arduino (C++)
lernen will muss man immer mehrere Quellen und Erklärungen nutzen, auch
bei Sachen die man meint schon verstanden zu haben.
Erschwerend kommt noch hinzu was Arduino Fanboy D. und Flo geschrieben
haben - es gibt kein festgelegtes und einzig wahres Vorgehen
gleichzeitig aber dummerweise einige festgelegte Regeln und entsprechend
viele Fallstricke und Missverständnisse, gerade als Anfänger der sein
Wissen nur aus Bücher und den Netz beziehen kann.
Jemand
Arduino Fanboy D. schrieb:> Mantra:> Eine vermeidbare globale Variable ist ein böse Variable.> Eine vermiedene globale Variable ist ein gute Variable.
Finde ich zu pauschal und verkürzt. Man kann auch ein großes Struct
machen, dort den ganzen Zustand reinpacken und das Ding jeder Funktion
mitgeben.
Dann habe ich keine globale Variable im Programm, aber genau das gleiche
Problem als hätte ich das Struct global: Niemand weiß wer wo wann was im
Programm verändert wird.
Sinnvolle Module, sinvolle Funktionen. Dazu gehört dass jede Funktion
genau die Daten bekommt die sie braucht.
Arduino Fanboy D. schrieb:> Funktionen sollten keinen eigenen inneren Zustand mit führen.> Das entspricht dem Prinzip der geringsten Verwunderung.
Hmm, wie kann das beispielsweise bei der Programmierung der Funktion
eines R/S Flipflop [1] funktionieren? Diese kennt man aus
Speicherprogrammierbaren Steuerungen. Sie bestehen aus zwei boolschen
Eingangs-Variablen "S(et)" und "R(eset) und einer boolschen
Ausgangsvariablen (sagen wir mit Namen "RS"). RS wird auf High gesetzt,
wenn S High ist und auf Low gesetzt, wenn R High ist. (Für den Fall,
dass beide Eingänge High sind, ist Reset dominant, was dadurch
sichergestellt werden kann, dass das Rücksetzen nach dem Setzen
geschieht.)
So kann beispielsweise ein Taster eine LED einschalten und ein anderer
Taster diese ausschalten. In jedem Loop muss der Zustand von R und S
geprüft und RS entsprechend gesetzt werden, um eine möglichst schnelle
Reaktion sicher zu stellen.
Wie soll der Code aussehen, wenn der innere Zustand von RS (High oder
Low) nicht mitgeführt wird?
[1] https://de.wikipedia.org/wiki/Flipflop#RS-Flipflop
Cyblord -. schrieb:> Finde ich zu pauschal und verkürzt.
Natürlich ist es das!
Das sind Leitsätze, und Stil Regeln, wohl immer.
Aber immerhin lässt es eine Art Hysterese zu.
In welcher die unvermeidbaren globalen Variablen stecken.
Ernie B. schrieb:> Hmm, wie kann das beispielsweise bei der Programmierung der Funktion> eines R/S Flipflop [1] funktionieren?
Wenn sich der Status des FlipFlop in der Funktion befindet, ist die
Funktion nicht mehrfach verwendbar. Es sei denn per "Copy and Paste" und
umbenennen.
Also Codeduplikate.
Statt externer Strukturen.
In C schon echt doof, aber in C++ gänzlich unnötig.
Cyblord -. schrieb:> Sinnvolle Module, sinvolle Funktionen. Dazu gehört dass jede Funktion> genau die Daten bekommt die sie braucht.
Richtig.
Und da macht es einen großen Unterschied, ob der Funktion das als
Parameter übergeben wird, oder sie selber auf globale/statische
Variablen zugreift.
Pauschal und vereinfacht: Das erste ist gut, das zweite ist böse.
Ernie B. schrieb:> Wie soll der Code aussehen, wenn der innere Zustand von RS (High oder> Low) nicht mitgeführt wird?
Mit einer globalen Variable :D
Nein. Es gibt Anwendungen, in denen ein static sinnvoll ist. Ich denke
z.B. auch an Filter-Funktionen, die die Anzahl der Werte und deren
Durchschnitt gerne selber speichern dürfen. Ich will dann nur noch einen
neuen Wert reinschieben und den gefilterten Wert rausbekommen.
Durch das Static ist die Funktion gekapselt und kann einfach kopiert und
im nächsten Projekt wieder verwendet werden, ohne dass man globale
Variablen mitnehmen muss.
Pauschal verteufeln oder pauschal alles mit static zuklatschen bringt
aber natürlich nichts.
Aber das FlipFlop ist doch ein schönes Beispiel, quasi aus dem Lehrbuch:
Man hat Daten (den Zustand) und man hat Funktionen die auf diesem
Zustand arbeiten.
In C ist hier die Standardvorgehensweise ein Struct für den Zustand. Ein
Variable vom Typ dieses Structs für jede FlipFlop Instanz und einen Satz
von Funktionen welche diese Variable per Pointer bekommen.
Und nun würde es auch absolut nichts ausmachen, wenn das Array der
FlipFlop Instanzen global abliegen würde. Solange darauf nur der Caller
der FlipFlop Funktionen zugreift um die richtige Instanz rauszuholen und
den Funktionen zu übergeben. Sich hier zu verkünsteln bringt dann mehr
Schaden als Nutzen. Der Code bleibt trotzdem klar und sauber.
Bei C++ nimmt einem die Sprache das meiste ab und man macht ein Objekt
um Daten (Felder) und Funktionen (Methoden) in einer Einheit zu
zusammenzuschließen. Das Array entfällt damit sowieso.
Somit ist jede Lösung mit static in einer Funktion unnötig und falsch.
Sebastian R. schrieb:> Durch das Static ist die Funktion gekapselt und kann einfach kopiert und> im nächsten Projekt wieder verwendet werden, ohne dass man globale> Variablen mitnehmen muss.
In C vielleicht.
Aber auch nur, wenn man pro Programm nur einen dieser Filter benötigt.
In C++ würde man besser ein Objekt/Klasse draus machen.
> Pauschal verteufeln oder pauschal alles mit static zuklatschen bringt> aber natürlich nichts.
Zustimmung!
Cyblord -. schrieb:> Somit ist jede Lösung mit static in einer Funktion unnötig und falsch.
Auch aus meiner Sicht.