Wertnamen in C
Einführendes Beispiel
In dem folgenden Programmbeispiel wird ein Name als Ausdruck verwendet.
main.c
#include <stdio.h> /* printf */
#include <errno.h> /* errno */int main( void )
{ printf
( "%d\n", errno ); }stdout
0
(Die Bedeutung von »errno« ist hier nicht wichtig.)
Syntax
Ein einfacher Name oder Bezeichner, wie beispielsweise »errno«, ist (etwas vereinfacht gesagt) eine Folge von Zeichen, die mit einem Buchstaben beginnt, dem weitere Buchstaben oder Ziffern direkt folgen können. An Stelle eines Buchstabens ist auch der Grundstrich »_« erlaubt.
Ein Bezeichner ist eine lexikalische Einheit.
- Bezeichner
a
A
Haus
Haus2
HAUS_2- Morphologische Diagramme (vereinfacht)
Bezeichner .----------------------------------------.
.------------------------. | .-----------------------. v
|-|--->| Bezeichnerstartzeichen |---'---.--->| Bezeichnerrestzeichen |---:---'--->|-|
'------------------------' ^ '-----------------------' |
'--------------------------------'Bezeichnerstartzeichen
.-----------.
|-|---.--->| Buchstabe |---.--->|-|
| '-----------' |
| .-. |
'------->( _ )-------'
'-'Bezeichnerrestzeichen
.------------------------.
|-|---.--->| Bezeichnerstartzeichen |---.--->|-|
| '------------------------' |
| .------------------------. |
'--->| Dezimalziffer |---'
'------------------------'Buchstabe
.----------.
|-|---.--->| Majuskel |---.--->|-|
| '----------' |
| .----------. |
'--->| Minuskel |---'
'----------'Majuskel
.-.
|-|---.--->( A )---.--->|-|
| '-' ^
| .-. |
'--->( B )---'
| '-' ^
| .-. |
'--->( C )---'
| '-' ^
| .-. |
'--->( D )---'
| '-' ^
| .-. |
'--->( E )---'
| '-' ^
| .-. |
'--->( F )---'
| '-' ^
| .-. |
'--->( G )---'
| '-' ^
| .-. |
'--->( H )---'
| '-' ^
| .-. |
'--->( I )---'
| '-' ^
| .-. |
'--->( J )---'
| '-' ^
| .-. |
'--->( K )---'
| '-' ^
| .-. |
'--->( L )---'
| '-' ^
| .-. |
'--->( M )---'
| '-' ^
| .-. |
'--->( N )---'
| '-' ^
| .-. |
'--->( O )---'
| '-' ^
| .-. |
'--->( P )---'
| '-' ^
| .-. |
'--->( Q )---'
| '-' ^
| .-. |
'--->( R )---'
| '-' ^
| .-. |
'--->( S )---'
| '-' ^
| .-. |
'--->( T )---'
| '-' ^
| .-. |
'--->( U )---'
| '-' ^
| .-. |
'--->( V )---'
| '-' ^
| .-. |
'--->( W )---'
| '-' ^
| .-. |
'--->( X )---'
| '-' ^
| .-. |
'--->( Y )---'
| '-' ^
| .-. |
'--->( Z )---'
'-'Minuskel
.-.
|-|---.--->( a )---.--->|-|
| '-' ^
| .-. |
'--->( b )---'
| '-' ^
| .-. |
'--->( c )---'
| '-' ^
| .-. |
'--->( d )---'
| '-' ^
| .-. |
'--->( e )---'
| '-' ^
| .-. |
'--->( f )---'
| '-' ^
| .-. |
'--->( g )---'
| '-' ^
| .-. |
'--->( h )---'
| '-' ^
| .-. |
'--->( i )---'
| '-' ^
| .-. |
'--->( j )---'
| '-' ^
| .-. |
'--->( k )---'
| '-' ^
| .-. |
'--->( l )---'
| '-' ^
| .-. |
'--->( m )---'
| '-' ^
| .-. |
'--->( n )---'
| '-' ^
| .-. |
'--->( o )---'
| '-' ^
| .-. |
'--->( p )---'
| '-' ^
| .-. |
'--->( q )---'
| '-' ^
| .-. |
'--->( r )---'
| '-' ^
| .-. |
'--->( s )---'
| '-' ^
| .-. |
'--->( t )---'
| '-' ^
| .-. |
'--->( u )---'
| '-' ^
| .-. |
'--->( v )---'
| '-' ^
| .-. |
'--->( w )---'
| '-' ^
| .-. |
'--->( x )---'
| '-' ^
| .-. |
'--->( y )---'
| '-' ^
| .-. |
'--->( z )---'
'-'Dezimalziffer
.-.
|-|---.--->( 0 )---.--->|-|
| '-' ^
| .-. |
'--->( 1 )---'
| '-' ^
| .-. |
'--->( 2 )---'
| '-' ^
| .-. |
'--->( 3 )---'
| '-' ^
| .-. |
'--->( 4 )---'
| '-' ^
| .-. |
'--->( 5 )---'
| '-' ^
| .-. |
'--->( 6 )---'
| '-' ^
| .-. |
'--->( 7 )---'
| '-' ^
| .-. |
'--->( 8 )---'
| '-' ^
| .-. |
'--->( 9 )---'
'-'- Neue, erweiterte vereinfachte Syntax
Ausdruck
.------------.
---.----------->| Literal |---------------------------.---->
| '------------' |
| .-. .------------. |
'--->( - )-->| Ausdruck |---------------------------'
| '-' '------------' |
| .------------. |
'----------->| Bezeichner |---------------------------'
| '------------' |
| .-. .------------. |
'--->( + )-->| Ausdruck |---------------------------'
| '-' '------------' |
| .-. .------------. .-. |
'--->( ( )-->| Ausdruck |-->( ) )-------------------'
| '-' '------------' '-' |
| .------------. .-. .----------. |
'----------->| Ausdruck |-->( / )-->| Ausdruck |----'
| '------------' '-' '----------' |
| .------------. .-. .----------. |
'----------->| Ausdruck |-->( + )-->| Ausdruck |----'
| '------------' '-' '----------' |
| .------------. .-. .----------. |
'----------->| Ausdruck |-->( - )-->| Ausdruck |----'
| '------------' '-' '----------' |
| .------------. .-. .----------. |
'----------->| Ausdruck |-->( * )-->| Ausdruck |----'
'------------' '-' '----------'
Semantik
Wir haben schon gesehen, daß eine Zahl im Quelltext durch ein Literal angegeben werden kann.
main.c
#include <stdio.h> /* printf */
int main( void )
{ printf
( "%d\n", 32767 ); }stdout
32767
Eine Zahl kann jedoch auch durch einen Namen angegeben werden, den wir in diesem Fall auch Zahlennamenausdruck oder kurz Zahlennamen nennen. (In diesem Fall ist damit aber nicht gemeint, daß ein Name immer für dieselbe Zahl stehen muß. Die Zahl wurde dem Name willkürlich zugeordnet; daher kann diese Zuordnung auch in einem anderen Fall für denselben Namen wieder eine andere sein.)
main.c
#include <stdio.h> /* printf */
#include <errno.h> /* errno */int main( void )
{ printf
( "%d\n", errno ); }stdout
0
Groß- und Kleinschreibung
Die Groß- und Kleinschreibung ist signifikant. Das heißt, daß »abc« beispielsweise als ein anderer Name angesehen wird als »aBc«.
Die implementationsdefinierten Typgrößen
Die Größe des Wertebereichs der numerischen Datentypen ist implementationsdefiniert.
Das heißt, daß jede C -Implementation es selber festlegen kann, wie groß der kleinste beziehungsweise der größte Wert des Datentyps »int« ist. Jedoch muß der kleinste Wert kleiner als »[-32766]« und der größte Wert größer als »[32766]« sein.
main.c
#include <stdio.h> /* printf */
#include <limits.h> /* INT_MIN */int main( void )
{ printf
( "%d\n", INT_MIN ); }- Protokoll
-2147483648
main.c
#include <stdio.h> /* printf */
#include <limits.h> /* INT_MIN */int main( void )
{ printf
( "%d\n", INT_MAX ); }- Protokoll
2147483647
- N2176 5.2.4.2.1 Sizes of integer types <limits.h>, Absatz 1, Satz 3
- Their implementation-defined values shall be equal or greater in magnitude (absolute value) to those shown, with the same sign.
- …
INT_MIN -32767
INT_MAX +32767
C ist eine abstrakte Programmiersprache. Das heißt, daß viele Details durch die Norm nicht genau festgelegt sind, sondern nur ein gewisser Bereich vorgegeben wird, in dem sie sich bewegen dürfen. Ein Programmierer der portable Programme schreiben will, darf nie vergessen, daß viele Eigenschaften der Programmiersprache nicht durch die von ihm zur Entwicklung verwendete Implementation festgelegt sind, sondern durch die Sprachnorm. Ein Programm, das voraussetzt, daß »[INT_MAX]« gleich »[2147483647]« ist, würde beispielsweise möglicherweise nicht auf allen Implementationen das gewünschte Verhalten zeigen.
Aufgrund dieser Abstraktheit ist es schwer, C zu erlernen und zu beherrschen. Viele wichtige Aspekte der Sprache kann man nicht durch Ausprobieren mit einer Implementation herausfinden, sondern nur durch Lesen der Sprachnorm. Selbst das Lesen von Lehrbüchern hilft nicht immer, da viele Lehrbücher wichtige Details der Sprache nicht vollständig oder nicht korrekt wiedergeben. Diese Eigenschaft teilt C mit der auf C aufbauenden Sprache C++.
Zum Vergleich: Verschiedene andere Programmiersprachen, wie beispielsweise Java geben einen Wertebereich für den Datentyp »int« fest vor, den alle Implementationen genau übernehmen müssen. Solche konkreten Programmiersprachen sind leichter zu erlernen und zu beherrschen, aber weniger effizient als C.
Das undefinierte Verhalten eines int-Überlaufs
Wenn das Ergebnis einer Berechnung einen Wert ergibt, der in dem Datentyp des Ergebnisses nicht dargestellt werden kann, so ist das Verhalten der Berechnung undefiniert. Daher haben die folgenden Programme undefiniertes Verhalten. Das Protokoll der für dieses Skriptum verwendeten C -Implementation darf daher nicht verallgemeinert werden! Es ist also durch die Sprachnorm nicht garantiert, daß die Auswertung des Ausdrucks »INT_MIN - 1« wieder einen großen positiven Wert ergibt, vielmehr kann bei dieser Auswertung irgendetwas passieren (wie auch bei einer Division durch Null).
main.c
#include <stdio.h> /* printf */
#include <limits.h> /* INT_MIN */int main( void )
{ printf
( "%d\n", INT_MIN - 1 ); }- Protokoll
2147483647
Auch für die Auswertung von »INT_MAX + 1« ist nicht garantiert, daß dies wieder einen sehr kleinen negativen Wert ergibt. Vielmehr ist auch hier das Verhalten undefiniert. Selbst, wenn man herausgefunden hat, daß eine Implementation dafür einen sehr kleinen negativen Wert ergibt, so hat man keine Garantie dafür, daß dies bei dieser Implementation immer so sein wird.
main.c
#include <stdio.h> /* printf */
#include <limits.h> /* INT_MIN */int main( void )
{ printf
( "%d\n", INT_MAX + 1 ); }- Protokoll
-2147483648
Man sieht, daß eine Implementation bei undefiniertem Verhalten nicht immer eine Fehlermeldung ausgibt. Trotzdem ist die Auswertung von »INT_MAX + 1« ein schwerer Fehler des Programmierers. Der Programmierer ist dafür verantwortlich, daß solche Auswertungen bei einem Ablauf seines Programmes niemals vorkommen. Das ist natürlich schwierig. Auch hier zeigt sich wieder, daß C eine Programmiersprache ist, die schwer gut zu beherrschen ist. Es gibt andere Programmiersprachen, bei denen das Verhalten in solchen Fällen genau definiert ist, und die deswegen einfacher zu verwenden sind.
- N2176 6.5 Expressions, Absatz 5 (Übersetzung und Original)
- Tritt bei der Auswertung eines Ausdrucks eine Ausnahmesituation auf (d.h. ist das Ergebnis nicht mathematisch definiert oder nicht im Bereich der darstellbaren Werte für seinen Typ), ist das Verhalten undefiniert.
- If an exceptional condition occurs during the evaluation of an expression (that is, if the result is not mathematically defined or not in the range of representable values for its type), the behavior is undefined.
Übungsaufgaben
/ RAND_MAX
Der Ausdruck »RAND_MAX« hat den Typ »int« (wenn sich über ihm die Zeile »#include <stdlib.h>« findet).
Geben Sie den Wert des Namens »RAND_MAX« unter der von Ihnen verwendeten C -Implementation aus (unter Verwendung der Zeile »#include <limits.h>« am Anfang der Quelldatei).