Funktionen und Variable - übergeben und Gültigkeit

  • Hallo Hans Glueck,


    nun, ich habe mich bei dem Beispiel nicht an den Lehrplan gehalten. Den kenne ich nur teilweise.

    Dennoch habe ich mir das ein oder andere angeschaut, weil es auch interessant ist, wie andere (in diesem Fall der René) die Dinge lösen. Seine Videos gefallen mir auch ausgesprochen gut, weil sie didaktisch gut gemacht sind.


    Mehrdimensionale Arrays wie "FOLGEN[4][5]" kommen in dem Kurs aber vor... das kommt dann noch. Meine ich jedenfalls.


    Es gibt ja so direkt eigentlich kein richtig oder falsch beim Programmieren. Wenn ein Programm am Ende tut was es soll ist es ja gut.


    Allerdings betrachte ich Quellcode auch ganz gerne unter dem Gesichtspunkt der Verständlichkeit und Wartbarkeit.


    Und Quellcode mit Variablen die nur aus einzelnen Buchstaben bestehen (mal abgesehen von Laufvariablen in Schleifen), die dann auch teilweise in unterschiedlichem Kontext im Programm verwendet werden, empfinde ich als schwer lesbar und schwer wartbar. Idealerweise sollte ein Quellcode auch für "Fremde" so verständlich sein, dass man nicht das gesamte Programm mit Kommentaren vollkleistern muss. Darum habe ich versucht ein anderes Beispiel einzubringen. Wenn es auch noch funktioniert... freut mich ;).


    Auch wenn "enum" (noch?) nicht in dem Kurs vorkommt, lohnt es sich, sich mit diesem Konstrukt auseinander zu setzen. Es ist es ganz praktisch weil man damit einen Datentyp schafft, der nur ganz bestimmte Werte annehmen kann. Das verringert die Fehleranfälligkeit im Programm. Macht man Fehler (z.B. mit dem Wertebereich), sagt einem der Compiler das, weil er es dann erkennen kann. Außerdem ist die Nutzung von sprechenden Bezeichnungen auch einfacher zu begreifen als pure Zahlenwerte, die eine bestimmte Bedeutung haben.


    C/C++ ist eine Compilersprache, da hat die Länge der Variablenbezeichnung keinen Einfluss auf die Programmgröße. Der Speicherplatzverbrauch ergibt sich bei Variablen aus den eingesetzten Datentypen, nicht aus den Namen.


    Wenn Du die Arduino IDE zum Programmieren benutzt kannst Du glaube ich das #include <Arduino.h> auch weglassen. Ich benutze eine andere Entwicklungsumgebung. Da ist es nötig, die Datei explizit einzubinden.

  • So, jetzt habe ich die Hard- und deine Software auf dem Uno installiert, und das Programm funktioniert richtig. Glückwunsch!

    Du hast einigen meiner Variablen bessere Namen gegeben, wodurch der Ablauf verständlicher wird. Das werde ich in meinen Code übernehmen.

    Du hast aber auch Anweisungen benutzt, die ich noch nicht kenne und erklären könnte. Diese Anweisungen das Programm vereinfachen zwar den Code, aber ich möchte vorläufig bei den Anweisungen bleiben, die ich selber verstehe. Also entweder in den Folgen des Arduino Kurses lerne, oder aus den Referenzen und Beispielen verstehen lerne.

    So verwendest Du zB. "enum", dass nicht einmal in den Referenzen der Arduino.cc gelistet ist. Ich weis zwar, dass man mit enum Aufzählungen/Reihenfolgen definieren kann, aber die genaue Syntax möchte ich richtig lernen, bevor ich sie in eigenen Programmen verwende.

    Dein Programm verwendet auch #include Arduino.h. Die Funktion dieser Bibliothek ist mir ebenfalls noch unbekannt.
    Ich weis auch, was Du da mit "const int FOLGEN[4][5] = { {STARTPIN, 7,PINANZAHL,100, 250}," usw. definierst, aber die verwendete Syntax ist mir noch fremd.


    Natürlich danke ich Dir für deine Mühe, aber mir ging es darum, die Übergabe von Variabeln in andere Programmteile und die Gültigkeit der Variablen zu verstehen. Dem "Lehrplan" wollte ich noch nicht soweit voreilen. ;)

    Wenn ich Schuhe und Strümpfe ausziehe, kann ich bis 20 zählen. ;)

  • Vielen Dank, dass Du meinen Code nochmal angesehen, und sogar bearbeitet hast.

    Die Werte 0, 1 und 2 habe ich absichtlich gleich in Konstanten (nl, e, z) festgelegt, weil sie öfters im Code gebraucht werden.

    Wenn ich einen Zähler zurücksetze, dann gebe ich ihm den Wert = e oder eben nl

    Ebenso nutze ich die Konstanten 0 und 1 um Flags zu setzen oder abzufragen.

    Diese Programmiertechnik habe ich so gelernt, als man noch mit wenig Speicher auskommen musste und bis heute programmiere ich so.

    Bei den alten Programmiersprachen war es auch erforderlich, die Variablen am Anfang zu deklarieren.

    Natürlich weis ich, dass das heute nicht mehr erforderlich ist, aber bei längerem Code ist es hilfreich, wenn man die verwendeten Variablen und Konstanten im Kopf des Programms nachschlagen kann, oder eben ändern möchte.
    Den Rundenzähler kann ich nicht mit der Konstanten umprogrammieren, sondern ich setze ihn, gesteuert von dem übertreffen eines definierten Zeitintervalls am Ender der Runde, auf "e", also 1 zurück. Ebenso fängt der kleinste Zuschlag auch wieder bei e, also 1 an.

    Statt Variable +e verwende ich jetzt auch Variable++. Man lernt eben dazu.


    Inzwischen läuft mein erweitertes Lauflicht auf einem Arduino Mega 2560 mit 15 LEDs. Dazu musste ich nur die Konstanten z und Num am Programmanfang anpassen, um die erste LED auf Pin 22 und Anzahl auf 15 LEDs umprogrammieren. :) Kein Zufall sondern Absicht.

    Die LED an Pin 2 (für den Blitz definiert) hat sich dadurch auch selbstständig auf Pin 22 umgestellt, die anderen mit Zahlen definierten Blitz-LEDs musste ich dagegen von Hand umstellen. So viel zum Vorteil von der Definition mit Zahlen. ;) Deshalb werde ich die Definition der Blitzer-LEDs auf z+ die Zahl umstellen, dann habe ich bei künftigen Änderungen dieses Problem nicht. Also zB. A= z+2 für die grüne LED-


    Die Funktion Signal hatte bei mir ohne die Übergabe der Variablen nicht funktioniert, deshalb habe ich die Variablen so übergeben.

    Ich werde es aber nochmal ohne die Übergabe versuchen, vielleicht hatte ich einen anderen Fehler im Code.


    Mit dem 80er Jahre BASIC liegst Du richtig. Ich habe Ende 1981 mit einem ZX81 angefangen, und der konnte nur Einbuchstaben-Variablen und hatte anfangs nur 1KB Speicher. Später bin ich dann auf einen TA-alphatronic Rechner mit CP/M80 umgestiegen, der ein komfortableres BASIC konnte und worauf auch Turbo-Pascal lief.

    Wie Du sehen konntest, benutze ich auch bedeutungsvolle Variablen wie Dauer, Zuschlag, Runde, Durchlauf, Blinker, Blitz, Pause, Meldung oder Richtung, wenn sich die Verwendung durch das Programm schleppt. Für Schleifen, Flags und kurzfristige Übergaben reichen mir die Einzelbuchstaben. die werden meist auch durch Kommentare oder die passende "Meldung" beschrieben.


    Jetzt bin ich gespannt auf deinen Programmcode, für den ich ebenfalls danke..

    Wenn ich Schuhe und Strümpfe ausziehe, kann ich bis 20 zählen. ;)

  • Du benutzt in deinem Code nahezu ausschließlich globale Variablen. Darum musst Du Dir über Rückgabewerte (die es in dem Programm eh nicht gibt) und Funktionsparameter eigentlich gar keine Gedanken machen. Die Übergabe der Parameter an die Funktion Signal ist überflüssig. Du könntest auch einfach void Signal() ohne Parameter definieren.


    Was ich etwas ungünstig finde ist, dass Du beispielsweise die Konstanten e und nl in unterschiedlichen Kontexten benutzt. Einmal anscheinend als Flags für die Umschaltung des Vorwärts- und Rückwärtslaufes aber auch um ein Zeitintervall hochzusetzen, die Beschleunigung zu setzen oder den Rundenzähler neu zu setzen. Da ist es besser, man benutzt gleich die Zahlen 0 und 1. Noch besser ist es, wenn man für jeden Zweck eigene Konstanten definiert die auch einen sprechenden Namen haben.


    Konstanten definiert man normalerweise am Anfang eines Programms um sie im Bedarfsfall nur an einer einzigen Stelle ändern zu können, ohne danach den ganzen Code querchecken zu müssen. Das wird aber konterkariert, wenn man eine Konstante zu unterschiedlichen Zwecken nutzt. Ist es beispielsweise nötig den Rundenzähler anzupassen und man ändert die Konstante, passt vielleicht die Rundenzählerlogik, aber der Vorwärts- Rückwärtslauf funktioniert nicht mehr. Dann muss man wieder den ganzen Code checken und spätestens dann mit zwei Konstanten arbeiten, weil es nicht mehr passt.


    Außerdem darf man für Variablen auch sprechende Namen benutzen, damit auch jemand der das Programm nicht geschrieben hat nachvollziehen kann wozu die überhaupt sein sollen. Dann kann man sich ne Menge Kommentare sparen.

    Diese Einbuchstaben-Listings erinnern mich sehr an Basic aus den 1980igern.



    Ich habe den Code mal etwas umgestellt. Es kann gut sein, dass er nicht mehr so funktioniert wie vorgesehen, weil sich Fehler eingeschlichen haben.

    Ich habe nur versucht den Code "lesbarer" zu gestalten


  • Definieren bedeutet eine Variable mit ihrem Datentyp zu deklarieren und einen Wert zuzuweisen. Wird bei der Deklaration kein Wert zugewiesen bekommt sie vom Compiler automatisch den Wert 0.

    Offenbar gibt es unterschiedliche Ansichten, was bestimmte Begriffe bedeuten.

    Sorry, ist schon klar.

    Ich hatte übersehen, dass ich bei der Deklaration der Variablen schon einen Wert zugewiesen, und sie dort ebenfalls definiert hatte.

    Auf jeden Fall habe ich der Variablen "Dauer" in der Funktion "void setup" einen neuen Wert zugewiesen und der neue Wert wird im Code in den anderen Funktionen so angenommen.

    Mit der Funktion "void setup" meine ich natürlich den ganzen Code, der in der darauf folgenden geschweiften Klammer steht.

    Wenn ich Schuhe und Strümpfe ausziehe, kann ich bis 20 zählen. ;)

  • Definieren bedeutet eine Variable mit ihrem Datentyp zu deklarieren und einen Wert zuzuweisen. Wird bei der Deklaration kein Wert zugewiesen bekommt sie vom Compiler automatisch den Wert 0.


    Dauer = random(mini, d); 


    ist eine simple Zuweisung eines Wertes zu einer bereits (global) definierten Variable. Keine Definition.


    Das Verhalten, welches Du als seltsam betrachtest ist vollkommen korrekt, weil Du es so programmiert hast :).


    Offenbar gibt es unterschiedliche Ansichten, was bestimmte Begriffe bedeuten.

  • Dauer ist global deklariert,

    Aber in der letzten Zeile des void setup steht:

    "Dauer = random(mini, d); Serial.println(); // Dauer = Zufallszahl 72 -115"

    Damit wird der Variablen der Wert der Zufallszahl zugewiesen. Das meinte ich mit definiert.

    Danach läuft die Funktion void loop an.

    Aus void loop wird die Funktion void Ausgabe aufgerufen,

    void Ausgabe gibt den aktuellen Wert der Variablen im seriellen Monitor an. Hinter dem ersten Zeitinterval wird diese Zufallszahl richtig angegeben.


    Wenn man sich die Konstanten am Anfang des Codes merkt kann man dem kommentierten Code auch folgen. Es ist fast Alles kommentiert.

    Wenn ich Schuhe und Strümpfe ausziehe, kann ich bis 20 zählen. ;)

  • Also der Sketch ist schon ziemlich unübersichtlich aber die Variable Dauer ist bei Dir auch global (Zeile 4). Und diese Variable wird in den von Dir angesprochenen Funktionen genutzt. In setup() wird keine Variable Dauer definiert, nur benutzt.


    Eine Funktion gibt nur einen Wert zurück, wenn darin ein Befehl return <wert>; vorhanden ist. Eine Funktion muss mit dem Typ des Rückgabewertes definiert werden (int, double, float usw.). Eine Funktion vom Typ void ist eine Funktion die "nichts" zurück gibt. Void bedeutet soviel wie "leer".


    Darum kann eine void Funktion per Definition keinen Wert zurückgeben. Wenn du in eine solche Funktion ein return <wert>; reinschreibst, dann wird es einen Compilerfehler geben.

  • Vielen Dank für diese Erklärung.

    "Paul" ist übrigens übrig geblieben, als ich den Sketch mit anderen Variationen der Variablen getestet habe.

    Jetzt funktioniert Paul mit der Deklaration vor dem Setup.

    Damit ist die Funktion meines Laauflicht12_Schleife5 aber unlogisch. Dort wird im void setup eine Zufallszahl (letzte Zeile) für die long-Variable "Dauer" erzeugt, die in void loop sowohl an void Ausgabe und an void Zaehlwerk ohne weitere Deklaration richtig übergeben wird.

    Wenn die Funktion void nie einen Wert zurück geben kann, dürfte mein Sketch nicht laufen.

    Wenn ich Schuhe und Strümpfe ausziehe, kann ich bis 20 zählen. ;)

  • Die Variable „paul“ ist nur in der Funktion setup() bekannt. Darum kann sie in loop() nicht funktionieren. Deshalb muss sie entweder global definiert werden, also „über“ allen Funktionen, in dem Block, wo auch die Konstanten definiert werden oder halt in loop(), wie du es dann wohl auch gemacht hast. Dann ist sie auch nur dort verwendbar.


    Du kannst auch global, in setup() und loop() eine paul Variable definieren. Das sind dann aber drei verschiedene Variablen (Speichebereiche) mit dem gleichen Namen.


    Das Konstrukt setup() und loop() ist kein C/C++ typisches. Es ist eine Spezialität des Arduino Frameworks. Eigentlich erdacht um es den Anwendern etwas leichter zu machen. Aber evtl. kann es auch etwas verwirren. Nur weil Variablen in setup() definiert werden, sind sie nicht für das gesamte Programm verfügbar, also NICHT global. Es sind einfach nur zwei C (Unter-) Funktionen.


    Der Kern eines C/C++ Programmes ist eigentlich eine Funktion Namens main() {}.

    Das wird im Arduino Framework aber vor dem Anwender verborgen.


    Was Deinen Sketch betrifft. Die Variable Blinker wird dort in Zeile 2 global deklariert. Darum lässt sie sich auch in allen darunter stehenden Funktionen ändern. Da muss nichts übergeben werden.

  • Vielen Dank für eure Antworten.

    Dieser Sketch funktioniert nur mit der dokumentierten Änderung.

    Der Anhang ist ein Sketch, der ein 12-LED Lauflicht ( Pin2-13) mit steigender und fallender Geschwindigkeit und Progression in zwei Laufrichtungen ansteuert. Der Sketch ist gut kommentiert und die Inhalte der Variablen und der Ablauf der Programmteile lassen sich auf dem Monitor beobachten.

    Man beachte, dass die Variablen Blinker, Dauer und Zuschlag in void Zaehwerk verändert werden.

    Blinker wird auch in void Signal verändert, obwohl die Variable nicht übergeben wird. Da es mehr als ein einfaches Lauflicht ist, lässt es sich besser mit aufgebauter Hardware und den richtigen LED-Farben (siehe PinMode) beobachten.

    Ich hoffe, es stört Euch nicht, dass ich lieber mit Konstanten als mit Zahlen im Programmcode arbeite, wenn die Zahlen im Code mehrfach verwendet werden. Im Arduino YT-Kurs von René bin ich in Folge 9, habe aber Vorkenntnisse aus anderen Programmiersprachen.

    Lauflicht12-_Schleife5_raufRunter.ino

    Wenn ich Schuhe und Strümpfe ausziehe, kann ich bis 20 zählen. ;)

  • Was Du da schreibst, glaube ich soo nicht. Wenn Du sagst eine Funktion void Zaehlwerk() würde veränderte Werte auch aktualisiert zurückgeben, kann das schon nicht stimmen, weil eine "void" Funktion nie Werte "zurückgibt". Allenfalls wurden die Parameter nicht als Wert, sondern als Pointer die Funktion übergeben und innerhalb der Funktion der Inhalt des Speichers auf den der Pointer zeigt, verändert.


    Die Gültigkeit von Variablen in C/C++ ist sehr strikt. Das was Du beschreibts passiert gerne, wenn Variablen mit identischen Namen lokal und global deklariert werden.


    Variablen die in in einem Block {} deklariert werden, sind auch nur dort bekannt oder gültig.



    Etwas genauer erklärt: http://www.coder-welten.de/ein…-und-sichtbarkeit-26.html

  • Die Programmiersprache C ist für Einsteiger etwas kompliziert im Umgang mit Variablen und der Übergabe von Werten an Funktionen. Da gibt es Werte, Pointer auf Werte und Referenzen auf Werte. Ich habe bewusst darauf verzichtet, ein spezielles Video zu den Variablen zu machen. Das wäre entweder zu stark vereinfacht und unvollständig oder zu langatmig und zu schwer verständlich.

    Ich hatte gehofft, dass die Einführung von Variablen und Parametern durch Verwendung in den einzelnen Experimenten klar genug ist. Wenn die Videos in der vorgegebenen Reihenfolge abgearbeitet werden und dabei auch immer die Begleitunterlagen durchgelesen werden, sollten nicht mehr all zu viele Fragen offen bleiben.

    Wenn doch, bitte immer hier im Forum nachfragen, sobald eine Unklarheit auftritt. Nicht warten, bis es zu kompliziert wird. Es ist auch sehr wichtig, den exakten Code anzugeben, bei dem das Problem auftritt.


    Manchmal ist es schwierig für mich zu erkennen, wo Einsteiger Probleme haben. Ich programmiere seit etwa 1976 und da betrachte ich vermutlich sehr vieles als selbstverständlich, was anderen völlig unlogisch erscheint.


    Also bitte immer mit konkreten Programmbeispielen nachfragen. So kann ich gezielt Erklärungen zum unverstandenen Code abgeben.


    Gruss

    René

  • Hallo,

    Manche Funktionen und Variablen machen mich beim Arduino verrückt.

    Schreibe ich " int paul=1023; " vor oder in das Setup, so will das Programm im Loop die Variable nicht kennen. Scheibe ich die gleiche Deklaration in void loop funktioniert das Programm.


    Einmal verlangt eine eigene Funktion die saubere Übergabe der Variablen wie:

    A=4, B=9, C= 14; Blitz=250, Pause=500;

    Signal(A,B,C, Blitz, Pause);

    und die Aufnahme der Variablen mit:

    void Signal(int A, int B, int C, long Blitz, long Pause)

    So definiert will die Übergabe von Variablen in einer anderen Funktion so nicht funktionieren und die Funktion übernimmt die Variablen mit dem nackten Aufruf:

    Zaehlwerk(); und die Funktion

    void Zaehlwerk() arbeitet mit den Variablen aus void loop und gibt veränderte Werte auch aktualisiert zurück.


    Meine Programme laufen zwar, aber ich möchte doch gern wissen, nach welcher Logik die Variablen immer gelten oder übergeben werden müssen.:?:

    Wenn ich Schuhe und Strümpfe ausziehe, kann ich bis 20 zählen. ;)