|
|
||
|
|
||
| .. Zahlen, Konstante, Variable | ||
|
|
||
|
In den Bits von Speicherzellen einer CPU
oder ihres Arbeitsspeichers lassen sich als binäre Ziffern
interpretierte 0,1-Folgen ablegen. So gesehen bezeichnen die
Begriffe Zahl, Zeichen, Konstante und Variableninhalt ziemlich
ähnliche Dinge. In allen Fällen handelt es sich um die
Abfolge von binären Ziffern, die in einem oder mehreren
aufeinander folgenden Speicherbytes abgelegt wurden. Die Anzahl
zusammenhängender Speicherbytes sind normaler Weise 1Byte,
2Byte oder vielfache von 2, also 4, 8Byte usw:
Zahlenformat: Fließkommazahl / Ganzzahl Die Interpretation des Inhalts einer Speicherzelle als Zahl kann auf zwei Wegen erfolgen. Einmal nach einer Theorie von Fließkommazahlen, der hier wegen der notwendigen Mathematik nicht vertieft nachgegangen wird und einmal nach der Theorie der (ganzen) Binärzahlen. In die letzte Kategorie fallen auch die Zeichen wie 'H' oder '#', denn bei ihnen handelt es sich auch nur um Zahlen, die aus der Vereinbarung einer Codetabelle, wie der ASCII-Tabelle hervorgehen. Veranschaulichung: Die Zahl 1234, als Binärzahl im Speicher abgelegt, belegt 2 Bytes und lautet codiert 04D2h. Die gleiche Zahl, abgelegt als doppelt genaue Fließkommazahl belegt 8 Bytes und lautet codiert 4093480000000000h. Der Vorteil der letzten Codierung als Fließkommazahl, liegt für diesen Fall nicht im benötigten Speicherplatz, sondern in dem Umstand, dass z.B. bei Divisionen Nachkommastellen erzeugt werden können, was bei Ganzzahlen nicht möglich ist. So ergäbe zum Beispiel die Ganzzahldivision 1234:4 =308 während die Fließkommadivision 308.5 hervorbringen würde. Zugriff auf den Inhalt einer Speicherzelle Hat man eine Zahl in einer Speicherzelle abgelegt, macht es Sinn, sich die Nummer (die Adresse) dieser Speicherzelle zu merken, damit man den Inhalt wiederfinden kann Dies geschieht in der Regel durch einen Variablennamen der intern für die Nummer (die Adresse) der Speicherzelle steht. Bei heinz =123; lautet die Anweisung vollständig. Suche die Speicherzelle mit der Adresse heinz im Hauptspeicher und fülle ihren Speicherraum mit der Zahl 123. Der binäre Inhalt der Speicherzelle heinz ist damit 0111 1011. Variable / Konstante Bei Variablen kann der Benutzer auf den Inhalt der Speicherzelle beliebig oft lesend und schreibend zugreifen. Es ist ihm also möglich den Inhalt seines Speichers während der Laufzeit des Programms zu ändern. Bei Konstanten, kann er nur einmal in den Speicher schreiben, dann kann er den Bereich nur noch lesen. Eine Konstante (Speicherzelle) kann also zur Laufzeit des Programms nicht mehr verändert werden. Speicherbedarf einer Variablen => Zahlenmenge Das Zusammenschalten mehrerer Bytes zu einer Speicherzelle kostet Speicherraum im Arbeitsspeicher. Soll beispielsweise ein Buch mit einigen Mio. Buchstaben im Computer bearbeitet werden, ist es schon wesentlich ob dessen Buchstaben in je einem ober zwei oder noch mehr Bytes abgelegt werden. Zur Ablage eines Zeichens nach der ASCII-Tabelle ist maximal ein Byte pro Zeichen erforderlich. Bei der Verwendung von Speicherzellen mit 4Bytes für je ein Zeichen wären also 75% des benutzten Arbeitsspeichers unnötig verschwendet. Wie viele Bytes für eine Speicherzelle zusammengeschaltet werden sollen hängt also von der benötigten Zahlenmenge eines Problems ab. Anders herum kann die Zahlenmenge von 1,2,4,8 Bytes berechnet werden indem man die Formel 2 Anzahl der Bits benutzt. Beispiel: 1 Byte = 8 Bit => 28 = 256 Zahlen. Da 0 auch eine Zahl ist, umfasst die maximale Zahlenmenge in einem Byte die Zahlen von 0 bis 255, wenn alle Zahle positiv gedacht sind. Sollen auch negative Zahlen vorkommen, muss 256 / 2 =128 gerechnet werden, wobei das Byte nun die Zahle von -128 bist +127 enthält. (Bei den positiven Zahlen ist wieder die 0 enthalten. 2 Byte = 16Bit => 216 = 65536 (positiv 0 bis 65535 | negativ+positiv -32768 bis 32767) |
||||||
|
Der
VariablenTyp
Wie aus den vorhergehenden Zeilen ersichtlich ist, muss der Programmierer dem Übersetzerprogramm seines Quelltextes, dem Compiler also, Angaben zu dem gewünschten Speicherbedarf und dem Zahlenformat machen. Nur er kennt den späteren Verwendungszweck seiner Zahlen. Beide Angaben zusammen umschreibt man in der Sprache C als Typ der Variablen. Die Angaben zum Typ werden in der Deklarationszeile einer Variablen vorgenommen. Es gibt die Binär-Typen char, short, int und long sowie die Fließkomma-Typen float, double, long double. Zudem gibt es den Modifizierer const (Konstant), der für alle Typenangaben möglich ist und aus einer Variablen eine Konstante macht. Weitere Modifizierer sind unsigned (ohne Vorzeichen) und signed (mit Vorzeichen) die nur für die Codierung von Ganzzahlen einsetzbar sind und neben den Natürlichen Zahlen (N) eben auch Ganze Zahlen (Z), also solche mit Vorzeichen, ermöglichen. Normalerweise besteht eine Angabe zum Variablentyp aus dem Modifizierer und der Typenangabe. Beispiel: unsigned int. Es gibt aber für einige Typen auch Kurzbezeichnungen. Im Beispiel würde unsigned den Ausdruck unsigned int ersetzen können. Eine spezielle Typangabe ist void (leer). Sie zeigt an, dass kein vorgegebener Typ benutzt wird. Diese Typangabe ist bei Funktionen und Zeigern zu finden und wird dort behandelt. Die nachfolgende Tabelle führt die Variablen-Grundtypen der Sprache C auf. |
||||||
|
|
||||||
| Binär-Codierung: | ||||||
| Modifizierer | Typenangabe | Bytes | Zahlenbereich | |||
| signed | char | 1 | -128 | 127 | ||
| unsigned | char | 1 | 0 | 255 | ||
| char | 1 | s.Voreinstellung | s.Voreinstellung | |||
|
char
von character (Zeichen) .. für ASCIICodes und kleine
Zahlen.
Achtung: wird nur char angegeben, entscheidet eine Voreinstellung des Compilers, ob signed- oder unsigned char gemeint ist. Das kann zu Verwirrungen bei einer Neucompilationen eines Quelltextes führen, wenn inzwischen die Voreinstellung geändert wurde, oder ein zweiter Compiler mit anderer Voreinstellung verwendet wird. Sinnvoll ist es, durch die Modifizierer signed und unsigned grundsätzlich anzusagen, welchen char-Typ man benutzen möchte. Diese Angabe überschreibt die Voreinstellung des Compilers. |
||||||
| signed | short | 1(2) | -128 | 127 | ||
| unsigned | short | 1(2) | 0 | 255 | ||
| .. je nach Rechner 1 oder 2 Bytes, für kleine Zahlen. Die Kurzangabe 'short' steht für 'signed short'. Die Angabe 'unsigned short' muss vollständig angegeben werden. Der Typ wird selten benutzt, denn er steht in Konkurrenz zu char und/oder int. | ||||||
| signed | int | 2 | -32.768 | +32.767 | ||
| unsigned | int | 2 | 0 | +65.535 | ||
| Der häufigst benutzte Ganzzahlentyp bei 16Bit CPUs. Die Kurzangabe 'int' steht für 'signed int'. Die Kurzangabe 'unsigned' für 'unsigned int' | ||||||
| signed | long | 4 | -2.147.483.648 | +2.147.483.647 | ||
| unsigned | long | 4 | 0 | +4.294.967.295 | ||
| Für große Ganzzahlen und als Speicher für Adressen(Zeiger). Die Kurzangabe 'long' steht für 'signed long'. Für 'unsigned long' gibt es keine Kurzform. Allerdings kann auch long int (int long) oder long unsigned geschrieben werden. | ||||||
|
|
|
|
||||
| Fliesskomma Codierung: | ||||||
| Modifizierer | Typenangabe | Bytes | Zahlenbereich | |||
| float | 4 | 3.4 E -38 | 3.4 E +38 | |||
| double | 8 | 1.7 E -308 | 1.7 E +308 | |||
| long | double | 10 | 3.4 E -4932 | 3.4 E +4932 | ||
| Die Fließkommafunktionen der statischen C-Bibliothek arbeiten mit dem Typ double. Für long double müssen eigene Funktionen geschrieben werden. | ||||||
| Anmerkung Bei der vorhergehenden Tabelle ist zu beachten, dass die Variablentypen der Sprache C mit einer Anzahl von Bytes im Hauptspeicher des Computers gleichgesetzt werden. Diese Gleichsetzung kann sich ändern wenn die verfügbaren Computer größer und schneller werden. Dann muss man wieder einmal das Handbuch der benutzten IDE lesen. | ||||||
|
|
||||||
| Beispiele für die Deklaration von Variablen | ||||||
int x1; // Deklaration
|
||
| der Compiler merkt sich, dass im Programm eine Variable mit dem Namen 'x1' auftreten soll, die dann 2 Bytes benötigt. Er merkt sich weiterhin, dass diese Variable binär-codierte Zahlen im Bereich von -32.768 bis +32.767 aufnehmen kann. Die negativen Zahlen werden im Zweierkomplement abgelegt. | ||
unsigned x2; // Deklaration
|
||
| der Compiler merkt sich, dass im Programm eine Variable mit dem Namen 'x2' auftreten soll, die dann 2 Bytes benötigt. Er merkt sich weiterhin, dass diese Variable binär-codierte Zahlen im Bereich von 0 bis +65535 aufnehmen kann. | ||
double wert; // Deklaration
|
||
| der Compiler merkt sich, dass im Programm eine Variable mit dem Namen 'wert' auftreten soll, die dann 8 Bytes benötigt. Er merkt sich weiterhin, dass diese Variable fliesskomma-codierte Zahlen aufnehmen soll und verwendet Fliesskomma-Umrechnungsfilter vor der Ablage der Zahlen in den 8 Bytes. |
|
Das
ewige Dilemma ...
Deklaration, Definition und Initialisation von Variablen |
||
|
||
|
||
|
||
double x; // Deklaration: Fliesskommazahl, 8 Bytes
// bei erstem Auftreten von x einrichten
double y; // Deklaration: Fliesskommazahl, 8 Bytes
// bei erstem Auftreten von y einrichten
y = sin(x); // Definition von y und x weil erstmaliges
// Auftreten der Variablen im Programm. Es muß
// Speicherplatz angefordert werden.
// y wird auch initialisiert. Es wird also ein
// Sinuswert in dem Speicher eingetragen.
// Die fehlende Initialisation von x
// wird sich in einem Berechnungsfehler zur
// Laufzeit auswirken. Der Speicher wird zwar
// eingerichtet (definiert), besitzt danach aber
// einen unbekannten Wert.
|
||
|
||
|
|
Weitere Beispiele |
|
|
|
|
|
int x1=5; |
// Deklaration, Definition und // Initialisation in einer Zeile |
||
| der Compiler merkt sich, dass im Programm eine Variable mit dem Namen 'x1' auftreten soll, die 2 Bytes benötigt. Er merkt sich weiterhin, dass diese Variable binär-codierte Zahlen im Bereich von -32.768 bis +32.767 aufnehmen kann. Die negativen Zahlen werden im Zweierkomplement abgelegt. Dann reserviert er die benötigten 2 Bytes im Speicher und schreibt die binär-codierte Zahl 5 hinein | |||
|
|
unsigned char a; |
// Deklaration |
|
|
|
a = 1234; |
// Definition mit Initialisation |
|
| Diese Zeilen führen zu einer Fehlermeldung. Der Compiler hat sich gemerkt, dass die Variable 'a' ein Byte benötigt, also Zahlen im Bereich 0 bis 255 aufnehmen kann. Die Definition wird in der nächsten Zeile mit einem Byte durchgeführt, dann soll der Speicher mit der Zahl 1234 initialisiert werden. Da 1234 größer ist als 255, reagiert der Compiler mit einer Fehlermeldung. | |||
|
|
unsigned a,b,c; |
// Mehrfachdeklaration in einer Zeile |
|
| Es werden die drei Variablen a, b, c als Ganzzahlen von 4 Byte erklärt, die Werte im Bereich von 0-65535 aufnehmen können. Hierbei werden die einzelnen Variablen durch ein Komma getrennt und der Befehl mit einem Semikolon beendet. | |||
const int ok=1; |
// Deklaration, Definition und Initi- // alisation einer Konstanten |
||
int x; |
// Deklaration |
||
x = ok + ok; |
// Definition und Initialisation von x |
||
| Hier wird in den 2-Byte-Speicher 'ok' die Zahl 1 eingeschrieben. Der Inhalt des Speichers kann nach dieser Initialisation nicht mehr verändert werden. Aber die Variable 'ok' steht immer für die Zahl 1. So führt die Zeile x = ok + ok; dazu, dass der Speicher x mit der Zahl 2 gefüllt wird. Eine andere Konstantendefinition wäre: .. const double pi = 3.1415927; | |||
double a,b,c,d; |
// Mehrfachdeklaration von Variablen in // einer Zeile |
||
| Der Compiler merkt sich, dass die Bezeichner a, b, c, d Zahlen aufheben sollen, die in das Fließkommaformat gewandelt werden müssen und jeweils 8 Bytes Speicher benötigen. Später im Programm werden die Bezeichner initialisiert. Die erklärten Speicherbytes werden eingerichtet. | |||
a = 12.9; |
// der Speicherbereich a erhält die // Fliesskommazahl 12.9 eingeschrieben |
||
b = 5; |
// der Speicherbereich b erhält nach einer // Wandlung Binärformat-Fließkommaformat // die Zahl 5.0 |
||
c = a+b; |
// c wird das Ergebnis 12.9 + 5.0 = 17.9 // zugeordnet |
||
e = 9 / 4; |
// Achtung: Hier wird zunächst der // Ausdruck 9 : 4 = 2 (Rest 1) im // Binärformat berechnet. |
||
// Das Ergebnis 2 wird in das // Fließkommaformat zu 2.0 gewandelt und // dann im Speicher e abgelegt. e enthält // also die Zahl 2.0 nicht 2.25 |
|||
| Soll bereits die Berechnung im Fließkommaformat durchgeführt werden, so lautet die Anweisung .. | |||
e = 9./ 4.; |
// Jetzt werden die Fliesskommazahlen 9.0 // und 4.0 geteilt. |
||
| Deklarationen, Definitionen und Initialisationen können in einer Zeile durchgeführt werden. Jede Deklaration, Definition - Initialisation muss dann von der nächsten, durch ein Komma getrennt werden. Es können so recht umfangreiche Ausdrücke entstehen. | |||
| Hinweis Im obigen Text wird gesagt, dass der Compiler Speicherraum vom Betriebssystem anfordert. Dies ist nicht ganz genau. Speicherraum kann ein Programm erst zu seiner Laufzeit vom Betriebssystem anfordern. Zu dieser Zeit ist der Compiler nicht mehr aktiv, denn es liegt doch schon eine .exe-Datei vor. Besser wäre es zu sagen, der Compiler erstellt Maschinencode, der zur Laufzeit Speicherraum anfordern kann. | |||
|
|
|
|
|
.de