C für PICs: Funktionen
        
      
    
    
    zurück zu
        C für PICs , C-Compiler , PIC-Prozessoren , Elektronik , Homepage
    
    C für PICs
      
      Grundlagen von C
      Variablen
          Funktionen
          Operationen
        Programmablaufsteuerung
      Arrays und Strings
      Pointer
      Strukturierte Typen
        PIC-spezifisches
    
    
        zurück zu C für PICs 
    
    
    Funktionen
    
    
    Der ausführbare Programmcode jedes C-Programms besteht aus
    Programmblöcken, die Funktionen genannt werden. So eine Funktion
    kann man aufrufen, dann wird der Code in dieser Funktion vom
    Prozessor abgearbeitet, und danach kehrt der Prozessor zum rufenden
    Programm zurück. Funktionen können wieder andere Funktionen
    aufrufen. Eigentlich könnte man auch den gesamten Code in einen
    einzigen Block (die main-Funktion
    eines C-Programms) schreiben,
    
    main()
      {
         
        anweisung1;
         
        anweisung2;
         
        anweisung3;
         
        anweisung4;
         
        anweisung5;
      }
    
    
    aber es gibt oft Programmschnipsel, die man an mehreren Stellen des
    Programms immer wieder benötigt.  Im folgenden Beispiel ist das
    die drei mal auftretende Gruppe von anweisung1 bis anweisung5.
    
    main()
      {
         
        anweisung1;
         
        anweisung2;
         
        anweisung3;
         
        anweisung4;
         
        anweisung5;
         
        _was_anderes;
         
        anweisung1;
         
        anweisung2;
         
        anweisung3;
         
        anweisung4;
         
        anweisung5;
         
        _noch_was_anderes;
         
        anweisung1;
         
        anweisung2;
         
        anweisung3;
         
        anweisung4;
         
        anweisung5;
         
        _ganz_was_anderes;
      }
    
    
    Anstatt diesen Code nun an jeder "bedürftigen" Stelle im
    Hauptprogramm einzufügen, schreibt man ihn nur ein mal in eine
    Funktion, und ruft dann diese eine Funktion an diesen Stellen
    jeweils auf. Das Programm wird dadurch kleiner, übersichtlicher und
    leichter zu pflegen.
    
    anweisung1bis5() 
      {
         
        anweisung1;
         
        anweisung2;
         
        anweisung3;
         
        anweisung4;
         
        anweisung5;
      }
        
      main()
      {
         
        anweisung1bis5();
         
        _was_anderes;
          anweisung1bis5();
         
        _noch_was_anderes;
          anweisung1bis5();
         
        _ganz_was_anderes;
      }
    
    
    
    Definition und Prototypen
    
    Jedes C-Programm enthält einen Haupt-Funktion mit dem Namen main. Beim Start des PICs
    (power-on oder reset) wird diese Funktion aufgerufen. Im PIC sollte
    die Funktion main nie zu
    ende gehen (Endlosschleife), passiert es doch, dann wird ein Reset
    ausgelöst. Alle anderen Funktionen bekommen vom Programmierer einen
    Namen zugeteilt. 
    Im Folgenden sehen wir ein kleines C-Programm, das aus einer main-Funktion sowie einer
    zusätzliche Funktion mit dem Namen fu1
    besteht. Die main-Funktion
    ruft die Funktion fu1 auf.
    Diese Funktion macht hier allerdings nichts, außer nach ihrer
    Beendigung (return) wieder
    zu main zurückzukehren.
    Wie wir sehen, endet damit auch main.
    In einem Personalcomputer wäre damit das Programm beendet, und das
    Betriebssystem übernimmt wieder die Kontrolle. Im PIC gibt es kein
    Betriebssystem, und es würde ein Reset erfolgen. Das ist hier noch
    ein Mangel, den ich später beheben werde.
    
    fu1()
      {
         
        return;
      }
      
      main()
      {
         
        fu1();
      }
    
    
    Im Folgenden vertausche ich die beiden Funktionen des Programms.
    
    main()
      {
         
        fu1();
      }
      
      fu1()
      {
         
        return;
      }
    
    
    Eigentlich
scheint
sich
    nichts geändert zu haben. Trotzdem enthält das Programm nun einen
    Fehler, auf den der Compiler stoßen wird. Der Compiler arbeitet des
    Quelltext von oben nach unten ab. Dabei stößt er innerhalb der main-Funktion
    auf den Aufruf der Funktion fu1.
    Diese kennt er aber noch nicht (während main immer bekannt ist). Weshalb er das Compilieren
    mit einem Fehler abbrechen wird. Funktionen sollten also immer
    definiert sein, bevor sie
    aufgerufen werden.
    Um dieses Problem zu entschärfen, gibt es Funktions-Prototypen. Das
    sind vorgezogene Kurzbeschreibungen der Funktionen, die eigentlich
    nur aus dem Funktionskopf bestehen. Sie stehen am Programmanfang,
    wenigstens aber vor dem ersten Aufruf der Funktion. Der Compiler
    kennt dann beim ersten Aufruf der Funktion wenigstens ihren Namen
    (und die Funktions-Parameter), was ausreichend ist. Es muss dann
    aber im späteren Programmverlauf zwingend noch die vollständige
    Funktionsdefinition enthalten sein, oder es wird wieder ein Fehler
    gemeldet.
    
    fu1();
      
      main()
      {
         
        fu1();
      }
      
      fu1()
      {
         
        return;
      }
    
    
    Etwas habe ich bisher weggelassen. Nun werde ich es ergänzen. An
    Funktionen kann man Argumente übergeben, und eine Funktion kann auch
    ein Argument zurückgeben. Beides wird gleich erläutert. Wird auf
    solche Übergaben verzichtet (wie in unserem Beispiel), dann muss
    jeweils der Platzhalter void an
    Stelle der Argumente eingetragen werden. Das Listing sieht dann so
    aus:
    
    void fu1(void);
      
      void main(void)
      {
         
        fu1();
      }
      
      void fu1(void)
      {
         
        return;
      }
    
    
    
      Argumentübergabe
    
    Innerhalb einer Funktionen werden normalerweise Daten verarbeitet.
    Diese Daten stehen natürlich in Variablen.
    Geeignet wären dafür globale Variablen, auf die von überall, also
    auch aus Funktionen heraus, zugegriffen werden kann.
    Wesentlich flexibler können Funktionen aber genutzt werden, wenn
    ihnen beim Aufruf ein Wert direkt übergeben werden kann. Genau dazu
    dienen die runden Klammern hinter dem Funktionsnamen. Hier hinein
    schreibt man beim Funktionsaufruf den zu übergebenden Wert. Das kann
    z.B. eine Zahl sein, oder eine Variable, oder irgendein anderer
    Ausdruck, der einen Wert ergibt. Damit das klappt, muss der Compiler
    aber wissen, welchen Typ dieser Wert hat. Diese Festlegung erfolgt
    im Kopf der Funktionsdeklaration, wo auch schon der Funktionsname
    festgelegt wurde. Im nachfolgenden Beispiel wird die Funktion so
    definiert, dass man ihr eine integer-Zahl beim Aufruf übergeben
    muss. Beim Aufruf der Funktion wird dann automatisch eine lokale int Variable mit dem
    übergebenen Wert angelegt. In unserem Fall ist das die lokale
    Variable a.  
    
    void fu1(int a)
      {
         
        return;
      }
    
    
    Die Funktion fu1 macht zwar noch gar nichts, aber ein Programm mit
    dieser Funktion könnte so aussehen:
    
    void fu1(int a);
      
      void
        main(void)
      {
            int b;
            b = 8;
         
        fu1(5);
            fu1(b);
            fu1(3+b);
      }
      
      void fu1(int a)
      {
         
        return;
      }
    
    
    Im oben stehenden Beispiel wird die Funktion fu1 drei mal
    aufgerufen. Jedes mal wird ihr ein integer-Wert als Parameter
    übergeben, um verschiedene Varianten zu demonstrieren. Man beachte,
    dass am Programmanfang wieder ein Funktionsprototyp der Funktion fu1
    steht, der ebenfalls die Definition der zu übergebenen Parameter
    enthält. 
    
    Man kann einer Funktion auch mehrere Parameter gleichen oder
    unterschiedlichen Typs übergeben:
    
    void fu1(int a, int b, float c);
      
      void
        main(void)
      {
         
fu1(5,
77,
        22.5);
      }
      
      void fu1(int a, int b,
        float c)
      {
         
        return;
      }
    
    
    Man beachte, dass die Parameterübergabe eine Daten-Einbahnstraße in
    die Funktion hinein darstellt. Eine Rückübergabe veränderter Werte
    aus der Funktion heraus ist damit nicht möglich.
    
    
    Argumentrückgabe
    
    Aber wie kann eine Funktion ein Ergebnis der Datenverarbeitung an
    den Rest des Programms zurückgeben? Zum einen gibt es da natürlich
    globale Variablen, die auch innerhalb einer Funktion mit Werten
    beschrieben werden können.
    Deutlich flexibler ist aber die Argumentrückgabe. Jede gerufene
    Funktion kann genau einen
    Wert eines Typs an
    die aufrufende Funktion zurückgeben. Dafür betrachtet man einfach
    die Funktion als Ausdruck eines bestimmten Typs. Der Typ der
    Funktion muss natürlich in der Funktionsdeklaration (und auch im
    Prototypen) festgelegt werden. Der Typbezeicher wird dazu in der
    Deklaration dem Funktionsnamen vorangestellt. In diesem Beispiel ist
    es int. 
    
    int fu1(int a)
      {
         
        return(5);
      }
    
    
    Die Funktion kann jetzt einen integer-Wert an die rufende Funktion
    zurückgeben. Das geschieht durch den return-Befehl. Dieser Befehl beendet die
    Funktionsabarbeitung. Der Wert des in der runden Klammer hinter return stehenden Ausdrucks wird
    zurückgegeben. Er muss natürlich den richtigen Typ (hier also int) haben. Die oben definierte
    Funktion wird also immer "5" liefern, was noch ein wenig primitiv
    ist.
    Ich habe gerade eine tolle Idee. Man kann den numerischen Wert
    zweier Ausdrücke zusammenfassen!! Ich nenne das "die Addition" und
    werde es als Softwarepatent
    anmelden. Zunächst werde ich aber eine Funktion schreiben, die
    die Summe zweier int-Werte
    bildet. Diese Funktion nenne ich passender Weise "summe".
    
    int summe(int a, int b);
      
      void
        main(void)
      {
            int c;
         
c
        = summe(5, 77);
      }
      
      int summe(int a, int b)
      {
         
        return(a+b);
      }
    
    
    Man sieht, dass an die summe-Funktion
    zwei int-Argumente
    übergeben werden müssen. Innerhalb der Funktion stehen diese Werte
    dann in den lokalen Variablen a
    und b. Der Typ des
    Rückgabearguments (also der Typ der Funktion) ist ebenfalls int. Innerhalb der Funktion
    steht ein return-Befehl,
    der die Summe von a und b zurückgibt.
    In main wird summe mit den
    integer-Argumenten 5 und 77 aufgerufen. Anstelle dieser Zahlen
    könnte man auch integer-Variablen einsetzen. Die summe-Funktion
    gibt
    die Summe von 5 und 77 an die rufende main-Funktion
    zurück. Dort wird das Ergebnis in der integer-Variablen c abgespeichert.
    
    
    
    -->
weiter
zu
        Operationen
    
    
    
      zurück
          zu C für PICs , C-Compiler , PIC-Prozessoren , Elektronik , Homepage
    
     
    
    Autor: sprut 
    erstellt: 01.10.2007
    letzte Änderung: 26.04.2015