Forum: Mikrocontroller und Digitale Elektronik Wo im Arduino Uno Programm Variable delarieren?


von Ernie B. (Gast)


Lesenswert?

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(int i=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?

von Einer K. (Gast)


Lesenswert?

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.

von Flo (Gast)


Lesenswert?

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.

von Sebastian R. (sebastian_r569)


Lesenswert?

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.

von Joachim B. (jar)


Lesenswert?

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)

: Bearbeitet durch User
von Einer K. (Gast)


Lesenswert?

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.

von Joachim B. (jar)


Lesenswert?

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?

von Sebastian R. (sebastian_r569)


Lesenswert?

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.

von Einer K. (Gast)


Lesenswert?

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++)

von Pennywise (Gast)


Lesenswert?

>Prinzip der geringsten Verwunderung

Das ist gut, merk ich mir :)

von Jemand (Gast)


Lesenswert?

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

von Cyblord -. (cyblord)


Lesenswert?

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.

von Ernie B. (Gast)


Lesenswert?

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

von Einer K. (Gast)


Lesenswert?

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.

von Sebastian R. (sebastian_r569)


Lesenswert?

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.

von Cyblord -. (cyblord)


Lesenswert?

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.

: Bearbeitet durch User
von Einer K. (Gast)


Lesenswert?

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.

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.