Grundlagen der Programmiersprache C für Studierende der Naturwissenschaften

Ähnliche Dokumente
Grundlagen der Programmiersprache C für Studierende der Naturwissenschaften

Verwendung Vereinbarung Wert einer Funktion Aufruf einer Funktion Parameter Rekursion. Programmieren in C

Programmiertechnik. Teil 4. C++ Funktionen: Prototypen Overloading Parameter. C++ Funktionen: Eigenschaften

Programmiersprachen Einführung in C

Grundlagen der Programmiersprache C für Studierende der Naturwissenschaften

Programmiersprache 1 (C++) Prof. Dr. Stefan Enderle NTA Isny

7 Funktionen. 7.1 Definition. Prototyp-Syntax: {Speicherklasse} {Typ} Name ({formale Parameter});

Programmierung und Angewandte Mathematik

2. Programmierung in C

Programmiersprachen Einführung in C. Unser erstes C-Programm. Unser erstes C-Programm. Unser erstes C-Programm. Unser erstes C-Programm

Variablen. Deklaration: «Datentyp» «Variablenname» Datentyp bestimmt Größe in Bytes: sizeof Beispiel: long int v; Größe: 4 Bytes

Tag 5. Repetitorium Informatik (Java) Dozent: Marius Kamp Lehrstuhl für Informatik 2 (Programmiersysteme)

2. Programmierung in C

C++ Teil 2. Sven Groß. 16. Apr IGPM, RWTH Aachen. Sven Groß (IGPM, RWTH Aachen) C++ Teil Apr / 22

Übung zur Vorlesung Wissenschaftliches Rechnen Sommersemester 2012 Auffrischung zur Programmierung in C++, 1. Teil

Methoden. Gerd Bohlender. Einstieg in die Informatik mit Java, Vorlesung vom

10 Die Programmiersprache C99: Zusammenfassung

Einführung in den Einsatz von Objekt-Orientierung mit C++ I

Elementare Datentypen in C++

Typ : void* aktuelle Parameter Pointer von beliebigem Typ

Programmieren in C. Eine Einführung in die Programmiersprache C. Prof. Dr. Nikolaus Wulff

2. Unterprogramme und Methoden

Einführung in die C-Programmierung

C-Kurs 2010 Pointer. 16. September v2.7.3

Wiederholung Wozu Methoden? Methoden Schreiben Methoden Benutzen Rekursion?! Methoden. Javakurs 2012, 3. Vorlesung

einlesen n > 0? Ausgabe Negative Zahl

Vorkurs C++ Programmierung

Einstieg in die Informatik mit Java

C++ - Funktionen und mehr -

Einleitung Entwicklung in C Hello-World! Konstrukte in C Zusammenfassung Literatur. Grundlagen von C. Jonas Gresens

Implementieren von Klassen

C-Crashkurs. Praktikum Systemmanagement und Sicherheit

1.4. Funktionen. Objektorientierte Programmierung mit C++

Einführung in die Programmiersprache C

DAP2 Praktikum Blatt 1

Vorlesungsprüfung Programmiersprache 1

Unterlagen. CPP-Uebungen-08/

Klassenvariablen, Klassenmethoden

Informatik I - Übung 2 Programmieren in Eclipse

4.4 Imperative Algorithmen Prozeduren

F Zeiger, Felder und Strukturen in C

Einstieg in die Informatik mit Java

C# - Einführung in die Programmiersprache Methoden. Leibniz Universität IT Services

2 Eine einfache Programmiersprache

Zeichendarstellung. Zeichen sind Zahlen (in C) Zeichen und switch

Methoden und Wrapperklassen

Java Einführung Methoden. Kapitel 6

Prozeduren vs. Funktionen

Systempraktikum im Wintersemester 2009/2010 (LMU): Vorlesung vom Foliensatz 2

Elementare Konzepte von

Martin Unold INFORMATIK. Geoinformatik und Vermessung

C++ - Objektorientierte Programmierung Konstruktoren und Destruktoren

Computergrundlagen Programmieren in C

Arrays (Felder/Vektoren)

Vorlesung Programmieren

Grundlagen der Programmierung in C Funktionen

Java 8. Elmar Fuchs Grundlagen Programmierung. 1. Ausgabe, Oktober 2014 JAV8

Probeklausur Programmieren in C Sommersemester 2007 Dipl. Biol. Franz Schenk 12. April 2007, Uhr Bearbeitungszeit: 105 Minuten

Übungspaket 29 Dynamische Speicherverwaltung: malloc() und free()

Zeiger. C-Kurs 2012, 2. Vorlesung. Tino Kutschbach 10.

Programmieren in C. Funktionen mit Zeigern und Adressen. Prof. Dr. Nikolaus Wulff

Die Programmiersprache C99: Zusammenfassung

Erste Schritte der Programmierung in C

Funktionen in JavaScript

Programmieren in C. Felder, Schleifen und Fließkommaarithmetik. Prof. Dr. Nikolaus Wulff

Informatik I. Übung 2 : Programmieren in Eclipse. 5. März Daniel Hentzen

Prof. Dr. Oliver Haase Karl Martin Kern Achim Bitzer. Programmiertechnik Kontrollstrukturen

Themen. Formatierte und unformatierte Eingabe Bedingungsoperator Namespaces Kommandozeilenargumente

Speicherklassen (1) Lokale Variablen

Grundlagen der Programmiersprache C für Studierende der Naturwissenschaften

Grundlagen der Programmierung Prof. H. Mössenböck. 6. Methoden

Globale Variablen Diverses. Globale Variablen. Globale Variablen

Grundlagen. Die Komponenten eines C Programms. Das erste Programm

Outline. 1 Einleitung. 2 Einführung in C. 3 Fortgeschrittenes in C. 4 Einführung in Emacs Lisp. 5 Einführung in Prolog. 6 Formale Semantik

C++ Teil 4. Sven Groß. 30. Apr IGPM, RWTH Aachen. Sven Groß (IGPM, RWTH Aachen) C++ Teil Apr / 16

C/C++-Programmierung

Einstieg in die Informatik mit Java

Programmiertechnik Kontrollstrukturen

Algorithmen zur Datenanalyse in C++

Felder (1) Felder (Arrays) speichern viele Datenelemente des gleichen Typs. Auf einzelne Elemente kann über einen Index zugegriffen werden

pue13 January 28, 2017

Folien zur AG: Objektorientiertes Programmieren in C++ Einheit 5: Funktionen. Michael Hahsler, Gottfried Rudorfer und Werner J.

Ein kleiner Blick auf die generische Programmierung

Variablen. int Flugzeug. float. I write code Hund. String. long. Borchers: Programmierung für Alle (Java), WS 06/07 Kapitel

Es ist für die Lösung der Programmieraufgabe nicht nötig, den mathematischen Hintergrund zu verstehen, es kann aber beim Verständnis helfen.

Einführung in die Programmierung Wintersemester 2010/11

Funktionen in JavaScript

FH München, FB 03 FA WS 06/07. Ingenieurinformatik. Name Vorname Matrikelnummer Sem.Gr.: Hörsaal Platz

Modul Entscheidungsunterstützung in der Logistik. Einführung in die Programmierung mit C++ Übung 4

Teil 5: Zeiger, Felder, Zeichenketten Gliederung

Funktionen Häufig müssen bestimmte Operationen in einem Programm mehrmals ausgeführt werden. Schlechte Lösung: Gute Lösung:

C++ - Einführung in die Programmiersprache Funktionen. Leibniz Universität IT Services Anja Aue

2. Programmierung in C

Zeichenketten (1) Literale von Zeichenketten werden in doppelte Anführungszeichen gesetzt

Funktionen, Zeiger und Arrays in C Software Entwicklung 1

Tag 4 Repetitorium Informatik (Java)

Vorlesung Objektorientierte Programmierung Klausur

C-Grundlagen. zur Programmierung des MicroControllersystems MiCoWi

Programmier-Befehle - Woche 10

Einführung in Java. Arne Hüffmeier. Angelehnt an Java-Vorkurs der Freitagsrunde. Methoden implementieren. Michelle Liebers, Dennis Hoffmann

Transkript:

Grundlagen der Programmiersprache C für Studierende der Naturwissenschaften Teil 5: Funktionen, Gültigkeitsbereiche und Rekursion Martin Nolte Abteilung für Angewandte Mathematik Universität Freiburg i. Br. Vorlesung vom 23. Mai 2016

Motivation: Binomialkoeffizienten Gegeben seien Zahlen n, k N mit k n. Der Binomialkoeffizient n über k ( n n! = k) k! (n k)! entspricht der Anzahl der k-elementigen Teilmengen einer Menge mit n Elementen. Um dies direkt zu berechnen, muss die Fakultät 3 mal ausgewertet werden: int fac_n = 1; for(int i = 2; i <= n; i = i+1) fac_n = fac_n * i; Allerdings wollen wir diese Schleife nicht 3 mal schreiben müssen.

Motivation: Binomialkoeffizienten Stattdessen schreiben wir eine Funktion für die Fakultät: int fac ( int n ) int fac_n = 1; for(int i = 2; i <= n; i = i+1) fac_n = fac_n * i; return fac_n; Unser Binomialkoeffizient könnte dann wie folgt aussehen: int nk = fac( n ) / (fac( k ) * fac( n - k ));

Inhalt Funktionen Die Funktion main Gültigkeitsbereiche Rekursive Funktionen Beispiel: Ein kleiner Taschenrechner

Funktionen Funktionen berechnen aus ihren Argumenten ein Ergebnis, den Rückgabewert. Funktionen können mehrfach und überall im Programm aufgerufen. Die Typen von Rückgabewert und Argumenten bilden den Prototyp der Funktion. Syntax: type 0 identifier 0 ( type 1 identifier 1,..., type n identifier n ) type 0 Typ der Rückgabewertes identifier 0 Name der Funktion type i Typ des i-ten Arguments identifier i Name es i-ten Arguments Die Argumentenliste kann auch leer sein.

Definition von Funktionen Eine Funktion wird definiert durch ihren Prototypen gefolgt von einem Anweisungsblock. Syntax: type 0 identifier 0 ( type 1 identifier 1,..., type n identifier n ) statement... Beispiel: int fac ( int n ) int fac_n = 1; for( int i = 2; i <= n; i = i + 1 ) fac_n = fac_n * i; return fac_n;

Die return-anweisung Syntax: return expression ; expression Ausdruck für den Rückgabewert Semantik: Verlasse die Funktion und gebe den Wert des Ausdrucks zurück. Der Typ des Ausdrucks muss mit dem Typ des Rückgabewertes der Funktion übereinstimmen. Ist Typ des Rückgabewertes void, muss der Ausdruck leer sein. Hinweis: Im Fall der Funktion main wird das Programm beendet.

Funktionsaufrufe Funktionsaufrufe sind elementare Ausdrücke. Syntax: identifier ( expression 1,..., expression n ) identifier Name der Funktion expression i Ausdruck für das i-te Funktionsargument Semantik: Rufe die Funktion mit den angegebenen Werten auf. Typ: Rückgabetyp der Funktion identifier. Wert: Rückgabewert der Funktion identifier. Die Typen der Ausdrücke müssen zu den Typen der Argumente im Funktionsprototypen passen. Achtung: Die Auswertungsreihenfolge der Argumente ist implementierungsabhängig.

Deklaration von Funktionen Funktionen müssen bekannt sein, bevor sie benutzt werden. Beispiel: 1 #include <stdio.h> 2 3 int print main () 4 5 printf( "The factorial of %d is %d", n, fac( n ) ); 6 7 8 int fac ( int n ) 9 10 int fac_n = 1; 11 for( int i = 2; i <= n; i = i + 1 ) 12 fac_n = fac_n * i; 13 printf( "The factorial of %d is %d", n, fac_n ); 14 return fac_n; 15 Falsch: Zum Zeitpunkt des Aufrufes ist der Prototyp der Funktion fac unbekannt.

Deklaration von Funktionen Wir können dem Compiler eine Funktion bekannt geben, indem wir sie deklarieren. Dazu schreiben wir den Prototypen der Funktion gefolgt von einem Semikolon. Syntax: type 0 identifier 0 ( type 1 identifier 1,..., type n identifier n ) ; Beispiel: int fac ( int n );

Implizite Deklaration von Funktionen Historisch mussten Funktionen nicht vor Benutzung deklariert werden: Sie wurden dann bei der ersten Verwendung implizit deklariert. Die Typen der Argumente kann der Compiler aus dem Aufruf ermitteln. Der Rückgabetyp wurde auf int festgelegt. Sie können zu zu schwer auffindbaren Fehlern führen. Seit C99 sind implizite Funktionsdeklarationen verboten. Warnung: Viele moderne Compiler (z.b. gcc Version 5) akzeptieren sie trotzdem. Sie geben lediglich eine Warnung aus.

Seiteneffekte Funktionen können Seiteneffekte haben. Beispiel: int fac ( int n ) int fac_n = 1; for( int i = 2; i <= n; i = i + 1 ) fac_n = fac_n * i; printf( "%d! = %d.\n", n, fac_n ); return fac_n; Hier wird bei jeder Berechnung das Ergebnis zusätzlich ausgegeben. Eine Funktion hat einen Seiteneffekt (engl. side effect), wenn Sie den Zustand des Programms verändert. Eine Funktion ohne Seiteneffekte heißt pur (engl. pure). In C gibt es keine Möglichkeit, Funktionen als pur zu kennzeichnen.

Der leere Datentyp void Manche Funktionen, etwa printf haben kein Ergebnis, sondern nur Seiteneffekte. Für diesen Fall gibt es den Datentyp void. Eine void-funktion muss nicht mit einer return-anweisung verlassen werden. Eine return-anweisung in einer void-funktion darf keinen Wert enthalten; sie lautet: return ; Beispiel: void print_fac ( int n ) printf( "%d! = %d.\n", n, fac( n ) );

Inhalt Funktionen Die Funktion main Gültigkeitsbereiche Rekursive Funktionen Beispiel: Ein kleiner Taschenrechner

Die Funktion main Beim Programmstart wird automatisch die Funktion main aufgerufen. Der Prototyp von main kann folgende Formen haben: int main () int main ( int argc, char *argv[] ) eine implementierungsabhängige Form Die zweite Form bekommt zusätzlich die Arguments, die auf der Kommandozeile angegeben werden. Ansonsten ist die Funktion main nicht besonders ausgezeichnet.

Programmargumente In der Form int main ( int argc, char *argv[] ) bekommt main die Arguments des Programms übergeben. argc Anzahl der Argumente (nichtnegativ) argv Feld der Länge argc von nullterminierten Strings Das erste Argument ist der Programmname (falls argc > 0). Er ist Implementierungsabhängig und kann leer sein (""). Das Programm darf die Stings in argv verändern.

Programmargumente Beispiel: #include <stdio.h> int main ( int argc, char *argv[] ) for( int i = 0; i < argc; i = i+1 ) printf( "argv[ %d ] = \"%s\"\n", i, argv[ i ] ); return 0; liefert folgende Ausgabe: $./args arg1 "argument 2" arg3 argv[ 0 ] = "./args" argv[ 1 ] = "arg1" argv[ 2 ] = "argument 2" argv[ 3 ] = "arg3"

Der Rückgabewert von main Der Rückgabewert von main gibt üblicherweise Auskunft über den Zustand des Programms bei Programmende. Die Datei stdlib.h definiert hierfür zwei Konstanten: EXIT_SUCCESS Das Programm wurde normal (erfolgreich) beendet. Unter Unix ist dieser Wert 0. EXIT_FAILURE Das Programm wurde aufgrund eines Fehlers beendet. Unter Unix kann dies eine beliebige, nichtverschindende Zahl sein. Die genaue Interpretation des Rückgabewertes ist nicht vorgeschrieben.

Inhalt Funktionen Die Funktion main Gültigkeitsbereiche Rekursive Funktionen Beispiel: Ein kleiner Taschenrechner

Gültigkeitsbereiche Variablen sind nicht global gültig, sondern nur in ihrem Gültigkeitsbereich (engl. scope). Der Gültigkeitsbereich einer (lokalen) Variablen ist der Block, in dem sie deklariert wurde. Außerhalb ihres Gültigkeitsbereiches ist die Variable nicht sichtbar und kann auch nicht verwendet werden. Beispiel: int s = 1; for( int i = 1; i <= 5; i = i + 1 ) int k = i*i; s = s + k; printf( "%d %d\n", s, k ); Fehler: k ist außerhalb des Schleifenrumpfes nicht mehr gültig.

Gültigkeitsbereiche Variablennamen können in jedem Block neu verwendet werden. In diesem Fall überdeckt die lokale Variable eine in einem umgebenden Block Gültigkeitsbereich (engl. scope). Beispiel: int s = 0; for( int i = 1; i <= 3; i = i + 1 ) int s = i*i; printf( "inner: %d\n", s ); printf( "outer: %d\n", s ); Ausgabe: inner: 1 inner: 4 inner: 9 outer: 0

Gültigkeitsbereich von Schleifenvariablen Beispiel: for( int i = 1; i <= 3; i = i + 1 ) printf( "inner: %d\n", s ); printf( "outer: %d\n", i ); Auch das ist falsch, denn die for-schleife ist äquivalent zu int i = 1; while( i <= 3 ) printf( "inner: %d\n", s ); i = i + 1; Also: Schleifenvariablen sind nur innerhalb der Schleife gültig.

Call-by-Value In C werden Argumente als Werte an Funktionen übergeben (call-by-value). Die Funktion kann sie nicht ändern. Beispiel: void initialize ( int a ) a = 0; int main () int a = 1; initialize( a ); printf( "a = %d\n", a ); /* a = 1 */ return 0; Die Funktion initialize besitzt eine Variable a, die mit dem Wert des Ausdrucks a initialisiert wird: 1. Offensichtlicher wird dies, wenn man beachtet, dass folgende Aufruf gültig ist: initialize( 1 ); Es gibt keinen Zusammenhang zwischen der Variablen a in initialize und der Variablen a in main.

Übergabe von Referenzen Manchmal möchte man aber Variablen beim Aufrufer verändern. In diesem Fall möchte man Referenzen übergeben (call-by-reference). In C muss man dies simulieren, indem man Zeiger auf die Variablen übergibt. Beispiel: void swap ( int *a, int *b ) int c = *a; *a = *b; *b = c; int main () int a = 1, b = 2; swap( &a, &b ); printf( "a = %d, b = %d\n", a, b ); /* a = 2, b = 1 */ return 0;

Übergabe von Feldern Eine Besonderheit stellen Felder als Argumente dar. Erinnerung: Ist a ein Feld vom Typ T, so ist der Ausdruck a vom Typ T. Beispiel: void f1 ( int x[] ); void f2 ( int x[ 5 ] ); void f3 ( int *x ); Alle drei Funktionsprototypen sind quivalent. Konsequenzen: Felder werden immer als Referenz übergeben. Die Anzahl der Feldelemente hat keinen Einfluss auf den Typ. Sie dient lediglich der Dokumentation.

Rückgabe von Feldern Funktionen können keine Felder zurückgeben. Lösungsmöglichkeiten: Das Feld für den Rückgabewert wird als Argument erwartet: void squares( int n, int s[] ) for( int i = 0; i < n; i = i + 1 ) s[ i ] = i*i; Man gibt einen Zeiger auf das Feld zurück (dynamische Speicherverwaltung). Man gibt einen benutzerdefinierten Datentyp zurück. Dynamische Speicherverwaltung und benutzerdefinierte Datentypen behandeln wir später.

Inhalt Funktionen Die Funktion main Gültigkeitsbereiche Rekursive Funktionen Beispiel: Ein kleiner Taschenrechner

Rekursive Funktionen Funktionen dürfen sich auch selber aufrufen. Damit lassen sich rekursive Algorithmen einfach implementieren. Beispiel: 1 double power ( double x, int n ) 2 3 if( n == 0 ) 4 return 1.0; 5 else 6 return x * power( x, n - 1 ); 7 Eine Rekursion braucht eine Abbruchbedingung (Zeile 2 3), sonst terminiert die Funktion nicht (vgl. Endlosschleife).

Rekursive Berechnung von Binomialkoeffizienten Die Formel ( ) n n! = k k! (n k)! ist für die Implementierung ungeeignet. Die Zahl n! wird schnell sehr groß: Bereits 13! = 6227020800 benötigt mehr als 32 Bit, um dargestellt zu werden. Hingegen ist ( ) 13 = 1716 relativ klein. 6 Die Binomialkoeffizienten lassen sich aber auch rekursiv berechnen. Für 0 < k < n gilt: ( ) ( ( ) n + 1 n n = +. k + 1 k) k + 1

Rekursive Berechnung von Binomialkoeffizienten Für 0 < k < n gilt: ( n = k) ( ) n 1 + k 1 ( ) n 1. k Beispiel: int binomial ( int n, int k ) if( (k == 0) (k == n) ) return 1; else return binomial( n-1, k-1 ) + binomial( n-1, k );

Divide and Conquer Klassisches Anwendungsfeld für Rekursion ist das Zerlegen großer Probleme in kleinere ( Teile und Herrsche ). Beispiel: /* let x be a sorted array */ /* find the position of i in the interval [begin,end) */ int binary_search ( int x[], int begin, int end, int i ) if( begin >= end ) /* empty interval */ return -1; const int mid = (end - begin) / 2; if( i == x[ mid ] ) return mid; if( i < x[ mid ] ) return binary_search( x, begin, mid ); else return binary_search( x, mid+1, end );

Endrekursion Ist der rekursive Aufruf die letzte (elementare) Instruktion, so sprechen wir von einer Endrekursion (engl. tail-recursion). Beispiel: void power_impl ( double x, int n, double *result ) if( n == 0 ) return; *result = *result * x; power_impl( x, n - 1, result ); double power ( double x, int n ) double result = 1.0; power_impl( x, n, &result ); return result;

Endrekursion Bei einer Endrekursion kann der rekursive Aufruf durch einen Sprung an den Funktionsanfang ersetzt werden. Beispiel: void power_impl ( double x, int n, double *result ) start: if( n == 0 ) goto end *result = *result * x; n = n - 1; goto start end: Dies ist äquivalent zu void power_impl ( double x, int n, double *result ) for( ; n!= 0; n = n - 1 ) *result = *result * x;

Endrekursion Eine Endrekursion ist damit äquivalent zu einer Schleife. Die Behauptung, Rekursion sei weniger effizient als eine Schleife ist daher i.a. falsch. Das Umwandeln einer Endrekursion in eine Schleife ist ein Beispiel für eine Compiler-Optimierung. Der übersetzte Code wird dadurch effizienter. Achtung: Unser ursprüngliches Beispiel ist nicht endrekursiv. double power ( double x, int n ) if( n == 0 ) return 1.0; else return x * power( x, n - 1 ); Hier muss nach dem Aufruf von power noch das Ergebnis mit x multipliziert werden.

Inhalt Funktionen Die Funktion main Gültigkeitsbereiche Rekursive Funktionen Beispiel: Ein kleiner Taschenrechner

Beispiel: Ein kleiner Taschenrechner Wir können jetzt bereits sehr mächtige Programme schreiben. Im folgenden soll ein kleiner Taschenrechner vorgestellt werden. Dieser liest einen Ausdrücke vom Benutzer, wertet diese aus und gibt das Ergebnis aus. Eigenschaften: Er beherrscht die Grundrechenarten. Er beachtet Punktrechnung vor Strichrechnung. Er bisitzt 26 Variablen (a z), die jederzeit zugewiesen werden können.

Grammatik Grammatik: expression assignexpr assignexpr, expression assignexpr letter = assignexpr addexpr addexpr mulexpr + addexpr mulexpr - addexpr mulexpr preexpr * mulexpr preexpr / mulexpr preexpr - preexpr + preexpr powexpr powexpr powexpr ^ terminal terminal terminal ( expression ) letter float mulexpr preexpr White Space soll ignoriert werden.

Eingabebeispiel $./calc 1+2*2^2 9.000000 a=4,b=3,a*b 12.000000 -(b-a)*(a+b) 7.000000 2^0.5 1.414214 i=1,j=1 1.000000 k=i,i=j,j=j+k 2.000000 k=i,i=j,j=j+k 3.000000 k=i,i=j,j=j+k 5.000000 k=i,i=j,j=j+k 8.000000 k=i,i=j,j=j+k 13.000000 $

Die Funktion main int main () double vars[ 26 ]; for( int i = 0; i < 26; i = i + 1 ) vars[ i ] = 0.0; while( true ) char line[ 256 ]; getline( line, sizeof( line ) ); if( line[ 0 ] == \0 ) return 0; double result; const char *end = evalexpr( line, vars, &result ); if( end[ 0 ]!= \0 ) printf( "Error: Trailing characters \"%s\".\n", end ); abort(); else printf( "%lf\n", result );

Die Funktion evalexpr Die Funktion evalexpr wertet einen Ausdruck aus. const char *evalexpr ( const char *s, double *vars, double *result ) s = evalassign( s, vars, result ); while( s[ 0 ] ==, ) s = evalassign( s+1, vars, result ); return s; Argumente: s Zeiger auf Anfang des auszuwertenden Ausdrucks vars Feld für die 26 Variablen result Zeiger auf Variable für das Ergebnis Rückgabewert: Zeiger auf erstes nicht ausgewertetes Zeichen Für jede Regel in der Grammatik gibt es eine solche Funktion.

Die Funktion evalterminal const char *evalterminal ( const char *s, double *vars, double *result ) if( s[ 0 ] == ( ) s = evalexpr( s+1, vars, result ); if( s[ 0 ]!= ) ) printf( "Error: \ )\ expected.\n" ); abort(); else return s+1; else if( (s[ 0 ] >= a ) && (s[ 0 ] <= z ) ) *result = vars[ s[ 0 ] - a ]; return s+1; else char *end; *result = strtod( s, &end ); if( end == s ) printf( "Error: Terminal expression expected.\n" ); abort(); return end;

Zwei weitere Funktionen als Beispiel const char *evalpowexpr ( const char *s, double *vars, double *result ) s = evalterminal( s, vars, result ); while( s[ 0 ] == ^ ) const double base = *result; s = evalterminal( s+1, vars, result ); *result = pow( base, *result ); return s; const char *evalmulexpr ( const char *s, double *vars, double *result ) s = evalprefixexpr( s, vars, result ); while( (s[ 0 ] == * ) (s[ 0 ] == / ) ) const double v = *result; const char op = s[ 0 ]; s = evalprefixexpr( s+1, vars, result ); if( op == * ) *result = v * (*result); else *result = v / (*result); return s;

Autoren Autoren die an diesem Skript mitgewirkt haben: 2011 2014 : Christoph Gersbacher 2014 2015 : Patrick Schön 2016 : Martin Nolte This work is licensed under a Creative Commons Attribution- ShareAlike 4.0 International (CC BY-SA 4.0) License. http://creativecommons.org/licenses/by-sa/4.0/legalcode