Funktionen variabler Arität in C
Die Ausgabeanweisung ohne Formatspezifizierer
Syntax Der Quelltext »printf( "\n" );« stellt ebenfalls eine Ausgabeanweisung dar.
Semantik Bei der Ausführung der Ausgabeanweisung »printf( "\n" );« wird ein Zeilenende ausgegeben.
- Ausgabeanweisungen
Anweisung
.------. .-. .----------------------. .-. .----------. .-. .-.
---->( printf )--->( ( )--->| Zeichenfolgenliteral |--->( , )--->| Ausdruck |--->( ) )--->( ; )--->
'------' '-' '----------------------' '-' '----------' '-' '-'Anweisung
.------. .-. .----------------------. .-. .-.
---->( printf )--->( ( )--->| Zeichenfolgenliteral |--->( ) )--->( ; )--->
'------' '-' '----------------------' '-' '-'main.c
#include <stdio.h>
int main( void )
{ printf( "%d\n", 1 );
printf( "%d\n", 2 );
printf( "%d\n", 1 );
printf( "\n" );
printf( "%d\n", 2 ); }stdout
1
2
1
2
Variable-Argumente-Funktionen
Die Funktion »printf« gehört zu einer speziellen Art von Funktionen, bei denen die maximale Anzahl der Argumente nicht festgelegt ist. »printf« muß mit mindestens einem Argument aufgerufen werden. Dieses erste Argument muß den Typ »char *« haben. Die Möglichkeit, weitere Argumente beliebigen Typs anzugeben, wird in der Dokumentation durch eine Ellipse »...« gekennzeichnet.
- Auszug aus der Dokumentation von »printf« (nach N1570, vereinfacht, überarbeitet und übersetzt)
- 7.21.7.4 Die Funktion »printf«
- Synopse
#include <stdio.h>
int printf( char * format, ... );
Variante der Ausgabeanweisung ohne Zeilenende
Es gibt eine Variante der Ausgabeanweisung, bei der das »\n« weggelassen wird. Dann wird die Zeile nicht abgeschlossen. Dies ist manchmal nützlich, wenn die Ausgabe in eine Zeile später noch fortgesetzt werden soll.
- eine abstrakte Ausgabeanweisung ohne Zeilenende (hier neu)
- printf( "%…", … );
main.c
#include <stdio.h>
int main( void )
{ printf( "%d", 4 );
printf( "%d\n", 12 ); }stdout
412
Ein einzelnes Zeilenende kann dann mit einer Ausgabeanweisung ohne Formatspezifizierer ausgegeben werden.
- Eine Ausgabeanweisung ohne Formatspezifizierer
- printf( "\n" );
main.c
#include <stdio.h>
int main( void )
{ printf( "%d", 4 );
printf( "\n" );
printf( "%d\n", 12 ); }stdout
4
12
Das voranstehende Programm hat dasselbe Verhalten wie das folgende.
main.c
#include <stdio.h>
int main( void )
{ printf( "%d\n", 4 );
printf( "%d\n", 12 ); }stdout
4
12
Der ordentliche Abschluß einer ausgegebenen Zeile
Wenn ein Programm überhaupt etwas als Text ausgibt, dann sollte die letzte Ausgabe des Programms ein Zeilenende sein, da jede Zeile mit einem Zeilenende abgeschlossen werden sollte.
- Zitat*
- 7.21.2 Streams
- …
- 2 A text stream is an ordered sequence of characters composed into lines, each line consisting of zero or more characters plus a terminating new-line character. Whether the last line requires a terminating new-line character is implementation-defined.
Da es von der Implementation abhängt, ob ein Zeilende als Abschluß der letzten Zeile einer Textausgabe benötigt wird, wird man nur dann allen Implementationen gerecht, wenn man die letzte ausgegebene Zeile immer mit einem Zeilenende abschließt.
Übungsfragen
? Übungsfrage
Welche Ausgabe erzeugt das folgende Programm?
(84: T, 85: U, 86: V, 87: W, 88: X)
main.c
#include <stdio.h>
int main( void )
{ printf( "", putchar( 86 )); }
? Übungsfrage
Welche Ausgabe erzeugt das folgende Programm?
(84: T, 85: U, 86: V, 87: W, 88: X)
main.c
#include <stdio.h>
int main( void )
{ printf( "W", putchar( 86 )); }
? Sprechweisen
- Was muß in dem folgenden Satz an der Stelle der ersten Lücke „__________“ eingesetzt werden? „ausgegeben“ oder „zurückgegeben“?
- Was muß in dem folgenden Satz an der Stelle der zweiten Lücke „__________“ eingesetzt werden? „ausgegeben“ oder „zurückgegeben“?
„In dem folgenden Programm wird ein Wert von »printf« __________, der von »rand« __________ wird.“
main.c
#include <stdio.h>
#include <stdlib.h>int main( void )
{ printf( "%d\n", rand() ); }stdout
41
? Zeilen angeben
Wie viele Zeilen gibt das folgende Programm aus, und was steht auf diesen Zeilen?
main.c
#include <stdio.h>
int main( void )
{ printf( "%d", 5 ); printf( "%d", 2 );
printf( "%d\n", 2 ); printf( "%d\n", 4 ); }
? Zeilen angeben (1)
Wie viele Zeilen gibt das folgende Programm aus, und was steht auf diesen Zeilen?
main.c
#include <stdio.h>
int main( void )
{ printf( "522\n4\n" ); }
Auswertung mit bekannter Reihenfolge (1)
In diesem Beispiel zeigen wir die einzelnen Operationen, die während der Auswertung des Ausdrucks »putchar( 1 + putchar( 71 ))« ausgeführt werden. (Zur Vereinfachung behandeln wir hier nur diesen Ausdruck, dessen Ausgabe »GH« ausgibt, und nicht auch noch die printf-Anweisung, welche dann noch »72« ausgibt.)
main.c
#include <stdio.h>
int main( void )
{ printf( "%d\n", putchar( 1 + putchar( 71 ))); }stdout
GH72
- Der Beispielausdruck
putchar( 1 + putchar( 71 ))
Bevor die erste Funktion »putchar« ausgeführt werden kann, muß zunächst der Wert des Argumentausdrucks »1 + putchar( 71 )« ermittelt werden.
Um den Wert der Summe »1 + putchar( 71 )« ermitteln zu können, muß zunächst der Wert des Summanden »putchar( 71 )« ermittelt werden.
So ergibt sich die Reihenfolge der folgenden drei Operationen
Operation 0 »putchar( 71 )«
- Funktionsoperation »putchar( 71 )«
- »putchar( 71 )« → »71« ↓ Ausgabe von »G«
Die Operation »putchar( 71 )« entspricht der Auswertung des Ausdrucks »putchar( 71 )«, sie ergibt den Wert 71 und bewirkt die Ausgabe eines »G«.
Operation 1 »1 + 71«
Nachdem nun der Wert 71 des rechten Summanden bekannt ist, kann die Addition ausgeführt werden.
- Operatoroperation »1 + 71«
- »1 + 71« → »72«
Die Operation »1 + 71« ergibt den Wert 71 und hat keine Wirkung. Diese Operation hat keine Wirkung.
Operation 2 »putchar( 72 )«
Nachdem nun der Wert »72« des Arguments der äußeren putchar-Aufrufs bekannt ist, kann die dazugehörige Operation »putchar( 72 )« ausgeführt werden.
- Funktionsoperation »putchar( 72 )«
- »putchar( 72 )« → »72« ↓ Ausgabe von »H«
Die Operation »putchar( 72 )« ergibt den Wert 72 und bewirkt die Ausgabe eines »H«.
Zusammenfassung
Der sich zuletzt ergebende Wert »72« wird dann schließlich noch vom Ausdruckrahmen ausgegeben. So kommt es dann insgesamt zur Ausgabe von »GH72«.
main.c
#include <stdio.h>
int main( void )
{ printf( "%d\n", putchar( 1 + putchar( 71 ))); }- Operationen während der Auswertung des Ausdrucks »putchar( 1 + putchar( 71 ))«
- »putchar( 71 )« → »71« ↓ Ausgabe von »G«
- »1 + 71« → »72«
- »putchar( 72 )« → »72« ↓ Ausgabe von »H«
stdout
GH72
Unspezifizierte Sequenzierung bei Argumenten
Ein Teilausdruck ist ein Teil eines Ausdrucks, der selber ein Ausdruck ist. Die Reihenfolge der Auswertung von Teilausdrücken ist in C im allgemeinen unspezifiziert.
“Except as specified later, side effects and value computations of subexpressions are unsequenced.” — C:2011:6.5p3
Beispielweise ist nicht spezifiziert, daß in dem folgenden Programm der Teilausdruck »putchar( 65 )« vor dem Teilausdruck »putchar( 66 )« ausgewertet wird.
main.c
#include <math.h>
#include <stdio.h>int main( void )
{ printf( "%g\n", pow( putchar( 65 ), putchar( 66 ))); }stdout
AB4.49036e+119
stdout
BA4.49036e+119
- Operationssequenz zu »pow( putchar( 65 ), putchar( 66 ))« (erste Möglichkeit)
- »putchar( 65 )« → »65« ↓ Ausgabe von »A«
- »putchar( 66 )« → »66« ↓ Ausgabe von »B«
- »pow( 65, 66 )« → »4.49036e+119«
- Operationssequenz zu »pow( putchar( 65 ), putchar( 66 ))« (zweite Möglichkeit)
- »putchar( 66 )« → »66« ↓ Ausgabe von »B«
- »putchar( 65 )« → »65« ↓ Ausgabe von »A«
- »pow( 65, 66 )« → »4.49036e+119«
Unspezifizierte Sequenzierung bei Operanden
Es ist nicht spezifiziert, daß in dem folgenden Programm der Teilausdruck »putchar( 65 )« vor dem Teilausdruck »putchar( 66 )« ausgewertet wird.
#include <math.h>
#include <stdio.h>int main( void )
{ printf( "%d\n", putchar( 65 )+ putchar( 66 )); }AB131
- Operationssequenz (erste Möglichkeit)
- »putchar( 65 )« → »65« ↓ Ausgabe von »A«
- »putchar( 66 )« → »66« ↓ Ausgabe von »B«
- »65 + 66« → »131«
- Operationssequenz (zweite Möglichkeit)
- »putchar( 66 )« → »66« ↓ Ausgabe von »B«
- »putchar( 65 )« → »65« ↓ Ausgabe von »A«
- »65 + 66« → »131«
Übungsfragen
? Typen und Wertebereich
Diese Übungsfragen gehören zum roten Faden „Würfeln“.
- Bei den folgenden beiden Übungsfragen wird die Inklusion des Headers »<stdlib.h>« vorausgesetzt.
- Wir haben jetzt gesehen, daß der Wert des Aufrufs »rand()« nicht immer gleich ist. Er kann gemäß der C -Norm irgendein 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 (mit steigendem Schwierigkeitsgrad) zu beantworten:
- In welchem Bereich liegt der Wert des Ausdrucks »( ( rand() ))«?
- In welchem Bereich liegt der Wert des Ausdrucks »rand()/10«?
- In welchem Bereich liegt der Wert des Ausdrucks »rand() - 10«?
- In welchem Bereich liegt der Wert des Ausdrucks »rand()/( 1. * RAND_MAX )«?
- In welchem Bereich liegt der Wert des Ausdrucks »rand()/( 1. + RAND_MAX )«?
- In welchem Bereich liegt der Wert des Ausdrucks »rand()/( 1. + RAND_MAX )* 10«?