Lesen
Eine Lesefunktion
.----------------------.
| Umgebung |
| |
| |
| |
| |
'----------------------'
Schreiben ^ |
| |
| v Lesen
.----------------------.
| Auswertung |
| |
| |
| |
| |
'----------------------'
Tip zum schnellen Lernen Versuchen Sie jetzt nicht, zu verstehen, was die »0« in »::std::perror( 0 )« bedeutet. Dies ist derzeit noch nicht wichtig! Vorläufig reicht es sich zu merken, daß bis auf weiteres als Argument immer »0« verwendet werden muß.
Die Auswertung von »::std::perror( 0 )« gibt den letzten zur Laufzeit aufgetretenen Fehler aus. Die Auswertung von »::std::ftell( 0 )« soll in dem folgenden Program einen Laufzeitfehler hervorrufen. (Also einen Fehler, der erst beim Ablauf der Programmes begangen wird.)
#include <cstdio>
int main()
{ ::std::perror( 0 );
::std::perror( 0 );
::std::perror( 0 );
::std::ftell( 0 );
::std::perror( 0 );
::std::perror( 0 );
::std::perror( 0 ); }
(Falls hier nach »::std::ftell( 0 )« kein Fehler gemeldet wird, könnten anderen Zahlen an Stelle von »0« in »::std::ftell( 0 )« probiert werden.)
Bei der Auswertung von »::std::perror( 0 )« wird ein Information über einen Fehler gelesen, die zuvor bei der Auswertung von »::std::ftell( stdin )« in den Speicher geschrieben wurde. Wir können sagen, daß diese beiden Auswertungen über einen Zustand kommunizieren.
- Kommunikation über die Umgebung (den Speicher)
::std::ftell .-----------------. ::std::perror .-----------------.
---------------------->| Speicher |---------------------->| Ausgabeflaeche |
Schreiben '-----------------' Lesen Schreiben '-----------------'
»::std::ftell« ist eine Funktion, die in den Speicher schreibt.
»::std::perror« ist eine Funktion, die aus dem Speicher liest und auf die Ausgabefläche schreibt.
Eine Leseauswertung ist eine Auswertung, die etwas einliest und deren Verhalten dann von dem Eingelesenen abhängt (wie beispielsweise eine Auswertung des Ausdrucks »::std::perror( 0 )«).
Laufzeitwerte ⃗
Man nennt Werte, die erst bei der Ausführung des Programms ermittelt werden, Laufzeitwerte. Das bedeutet, daß nicht schon der Aufrufausdruck den Wert festlegt, sondern erst der Aufrufvorgang. (Dies wurde zuvor schon als „Nicht-Determinismus“ und „Messung“ behandelt.) Derselbe Aufrufausdruck kann bei zwei verschiedenen Aufrufvorgängen (wie oben), also zur Laufzeit, zwei verschiedenen Werte haben. Der Aufrufausdruck steht im Quelltext für den Laufzeitwert, der sich bei jeweils einem Aufrufvorgang ergibt.
Der Aufrufausdruck »::std::time( null )« steht immer für dieselbe Operation, aber diese Operation kann bei verschiedenen Ausführungen, verschiedene Werte für den Aufrufausdruck liefern. Durch eine Aufrufausdruck kann also ein Laufzeitwert angegeben werden, während ein Literal immer denselben Wert hat.
Im Grunde sind alle Werte in C++ Laufzeitwerte, aber einige dieser Laufzeitwerte sind dann doch schon durch den Quelltext festgelegt, wie beispielsweise die Werte von Literalen, weil bei ihnen jede Auswertung zum selben Wert führt.
Wirkfunktion »::std::srand«
Der Aufruf von »::std::srand« stellt den Zufallszahlengenerator auf einen Ausgangswert ein, der durch den Argumentwert des Aufrufs festgelegt wird. Dieser Wert bestimmt dann die nächsten Zufallszahlen, welche »::std::rand« liefert. So kann verhindert werden, daß bei Programmstart immer dieselben Zahlen erscheinen.
Wir sehen hier das wichtige Prinzip, daß es einen von mehreren Funktionen geteilten Zustand gibt, auf den von außen nicht direkt zugegriffen werden kann. Wir haben eine Wirkfunktion, welche die Veränderung dieses Zustandes erlaubt und eine Wertfunktion (Meßfunktion), deren Verhalten von diesem Zustand beeinflußt wird.
- »::std::srand« (vereinfachte und übersetzte Dokumentation)
#include <cstdlib>
void ::std::srand( int a );
- Der Aufruf von »::std::srand« stellt den Zufallszahlengenerator auf den Ausgangswert »a« ein.
- ? »::std::srand«
- Die folgenden beiden Fragen sollen an Hand der oben abgedruckten Dokumentation beantwortet werden.
- Ergibt die Auswertung des Ausdrucks »::std::srand( 0 )« einen Wert?
- Hat die Auswertung des Ausdrucks »::std::srand( 0 )« eine Wirkung?
- / »::std::srand«
- Schreiben Sie ein Programm, in dem ein Wert des Ausdrucks »::std::rand()« ausgegeben wird.
- Welcher Wert wird ausgegeben, wenn »::std::srand( 10 )« direkt vor der Ausgabe von »::std::rand()« ausgewertet wird?
- Welcher Wert wird ausgegeben, wenn »::std::srand( 1 )« direkt vor der Ausgabe von »::std::rand()« ausgewertet wird?
.----------------------------------------------------------------------------------------.
| Welt |
| (Speicher, Uhr) |
| |
| |
| |
'----------------------------------------------------------------------------------------'
^ ^ ^
| | |
| v v
.----------------------. .----------------------. .----------------------.
| Auswertung von | | Auswertung von | | Auswertung von |
| ::std::srand( 10 ) | | ::std::rand() | | ::std::rand() |
| | | | | |
| | | | | |
| | | | | |
'----------------------' '----------------------' '----------------------'Zeit --->
Informationsübertragung
Wir haben jetzt insgesamt zwei Wege kennengelernt, auf denen Informationen von einem Aufruf zu einem anderen übertragen werden können:
- Sequenzen von Aufrufen wie »::std::srand( 87 ); ::std::rand();«: Hier verändert die Auswertung des ersten Aufrufs (»::std::srand( 87 )«) ein System, dessen Zustand dann bei der Auswertung folgenden Aufrufs (»::std::rand()«) eingelesen und berücksichtigt wird (Wirkverbindung ).
- Verschachtelte Aufrufe wie »::std::putchar( ::std::getchar())«: Hier liefert die Auswertung des inneren Aufrufs (»::std::getchar()«) einen Wert, der dann bei der Inkarnation des äußeren Aufrufs (»::std::putchar…«) als Argumentwert übergeben wird (Wertverbindung ). Es ist außerdem bei solchen Aufrufen möglich, daß es zusätzlich auch noch eine Wirkverbindung von der ersten Operation zur zweiten gibt.
Die Wertverbindung findet man auch in der Mathematik. Die Wirkverbindung wird in der Mathematik selten verwendet.
Wertfunktion »::std::time«
Der Aufruf von »::std::time« liefert eine Zahl, die sich im Laufe der Zeit verändert. Meist wird jede Sekunde um 1 hochgezählt.
- »::std::time« (vereinfachte und übersetzte Dokumentation)
#include <ctime>
int ::std::time( int );
- Ergibt eine Zahl, die sich im Laufe der Zeit verändert. Muß immer mit dem Argumentausdruck »nullptr« aufgerufen werden (vereinfacht gesagt).
- ? Übungsfragen zu »::std::time«
- Ergibt die Auswertung des Ausdrucks »::std::time( nullptr )« einen Wert?
- Hat die Auswertung des Ausdrucks »::std::time( nullptr )« eine Wirkung?
Tip zum schnellen Lernen Versuchen Sie jetzt nicht, zu verstehen, was »nullptr« in »::std::time( nullptr )« bedeutet. Dies ist derzeit noch nicht wichtig! Vorläufig reicht es sich zu merken, daß bis auf weiteres als Argument immer »nullptr« verwendet werden muß.
time.cpp
#include <iostream>
#include <ostream>
#include <ctime>
#include <string> // ""susing namespace ::std::literals;
int main()
{ ::std::cout << ::std::time( nullptr )<< "\n"s;
::std::cout << ::std::time( nullptr )<< "\n"s; }- / Zufallszahlen verbessern
- Kombinieren Sie den Ausdruck »::std::time( nullptr )« mit der Wirkfunktion »::std::srand«, um zu erreichen, daß der Pseudozufallszahlengenerator durch die Uhrzeit beim Start des Programmes beeinflußt wird.
- Geben Sie danach einige Zufallszahlen, die durch eine Aufruf von »::std::rand« erhalten wurden, aus, um den Effekt sichtbar zu machen. In »::std::time( nullptr )« sollte »nullptr« so belassen und nicht durch etwas anderes ersetzt werden!
- »::std::time« (vereinfachte und übersetzte Dokumentation)
#include <ctime>
int ::std::time( int );
- Ergibt eine Zahl, die sich im Laufe der Zeit verändert.
- »::std::srand« (vereinfachte und übersetzte Dokumentation)
#include <cstdlib>
void ::std::srand( int a );
- Der Aufruf von »::std::srand« stellt den Zufallszahlengenerator auf den Ausgangswert »a« ein.
- Dokumentation von »::std::rand()« (gekürzt und überarbeitet)
#include <cstdlib>
int rand();
- Ergibt eine Pseudo-Zufallszahl zwischen »0« (einschließlich) und »RAND_MAX« (einschließlich)
Es ist typisch für C++ und für das Programmieren insgesamt, daß man keine fertige Funktion für unvorhersehbare Zufallszahlen erhält, sondern diese sich aus einzelnen Bausteinen selber zusammensetzen muß. Würde es nämlich für alle vorstellbaren Zwecke bereits fertige Funktionen geben, dann wären dies so viele Funktionen, daß man die Übersicht verlieren würde.
Programmieren heißt, ein Ziel durch Zusammenfügen vorhandener Grundelemente zu erreichen.
Übungsfragen
- ? = Wertebereich
- Bei den folgenden beiden Übungsfragen wird die Verwendung der Präprozessor-Direktive »#include <cstdlib>« vorausgesetzt.
- Obwohl der Wert des Aufrufs »::std::rand()« oben immer als 41 angenommen wurde, könnte er gemäß der C++ -Norm auch irgendein anderer Wert zwischen 0 und RAND_MAX sein. Solch eine Unbestimmtheit des genauen Wertes kommt öfter vor. Deswegen ist es hilfreich, zur Übung im Umgang mit ihr die folgenden Fragen zu beantworten:
- In welchem Bereich liegt der Wert des Ausdrucks »::std::rand()/10«?
- In welchem Bereich liegt der Wert des Ausdrucks »::std::rand() - 10«?
- In welchem Bereich liegt der Wert des Ausdrucks »::std::rand() / RAND_MAX«?
- In welchem Bereich liegt der Wert des Ausdrucks »::std::rand()/( 1. * RAND_MAX )«?
Zusatzfragen
- ? = Wertebereich
- In welchem Bereich liegt der Wert des Ausdrucks »::std::rand()/( 1. + RAND_MAX )«?
- In welchem Bereich liegt der Wert des Ausdrucks »::std::rand()/( 1. + RAND_MAX )* 10«?