Grundlagen maschinennaher Informationsdarstellung
Die aktiven Elemente in digitalen Schaltungen (Transistoren in CPUs, Speicher, Controller etc.) können nur in den beiden Zuständen "ein" oder "aus" betrieben werden. Damit ergibt sich in natürlicher Weise eine binäre (zweiwertige) Darstellung der Information. Jede Information muss somit in einer Folge der Zustände ein/aus oder 1/0 ausgedrückt werden.
"Geschriebene" Information wie zum Beispiel dieser Text wird durch eine Folge von Buchstaben, Ziffern und Sonderzeichen ausgedrückt. Man verwendet eine Reihe von Symbolen, um alle diese Buchstaben, Ziffern und Sonderzeichen darstellen und voneinander unterscheiden zu können. Um diese Symbole auch in einem Computer verwenden zu können, benötigt man einen Code, der den Symbolen unterschiedliche Zahlenwerte zuordnet. Natürlich gibt es viele verschiedene Möglichkeiten, einen solchen Code zu erstellen. Tatsächlich gibt es auch mehr als einen Code für die Zuordnung von Zeichen zu Zahlenwerten. Einer dieser Codes ist der so genannte ASCII (American Standard Code of Information Interchange)-Code.
Dezimal- und Binärsystem
Die uns geläufigen Dezimalzahlen werden mit Hilfe der zehn Ziffern 0 bis 9 dargestellt. Es stehen also zehn verschiedene Zeichen oder Zustände für die Darstellung zur Verfügung. Dem Computer stehen aber nur zwei Zustände zur Verfügung, nämlich 0 und 1. Um die Zahlendarstellung in einem Computer zu verstehen, müssen wir zunächst analysieren, wie eine Dezimalzahl interpretiert wird.
Unser Zahlensystem wird auch als Stellensystem bezeichnet (etwa im Gegensatz zu den römischen Zahlen), weil jede Stelle einer Dezimalzahl einem bestimmten Wert entspricht. Wenn wir zunächst nur natürliche Zahlen betrachten, dann weist die am weitesten rechts stehende Stelle der Wert eins, die zweite Stelle von rechts den Wert zehn, die dritte Stelle von rechts den Wert hundert usw. auf. Die Anzahl solcher Werte je Stelle wird durch die an der betreffenden Stelle stehende Ziffer angegeben.
Beispiel:
Interpretation der Zahl 4711
1*1 + 1*10 + 7*100 + 4*1000
oder in Potenzen ausgedrückt
1*100 + 1*101 + 7*102 + 4*103
Dieses Schema lässt sich für beliebig große Zahlen erweitern. Die Zahl 10 stellt dabei die Basis des Zahlensystems dar (daher der Begriff Dezimalzahlen); sie stimmt mit der Anzahl unterschiedlicher Ziffern überein. In einem Computer stehen aber nicht zehn, sondern nur zwei solcher Ziffern zur Verfügung. Daher muss die Zehn durch zwei ersetzt werden. Das führt uns zum Dual- oder Binärsystem. Zahlen im Binärsystem werden durch die Ziffern 0 und 1 dargestellt.
Beispiel:
Die binäre Zahl 1001001100111 bedeutet
1*20 + 1*21 + 1*22 + 0*23 + 0*24 + 1*25 + 1*26 + 0*27 + 0*28 + 1*29 + 0*210 + 1*212
Wir erhalten somit zwei verschiedene Darstellungen der Zahl 4711: 4711 im Dezimalsystem und 1001001100111 im Binärsystem. Auch das binäre Schema lässt sich wie die Dezimalzahlen zu beliebig großen Werten fortsetzen. Man erkennt aber sofort einen großen Nachteil des Binärsystems gegenüber dem Dezimalsystem: die Zahlen werden erheblich länger, d.h. man benötigt sehr viel mehr Platz, um sie aufzuschreiben.
In diesem Zusammenhang werden zwei wichtige Begriffe verwendet: Eine binäre Ziffer oder Stelle im Binärsystem heißt Bit. Acht Stellen, d.h. acht binäre Ziffern werden zu einem Byte zusammen gefasst. Mit einem Byte lassen sich insgesamt 28 = 256 verschiedene Zahlen darstellen, und zwar die dezimalen Zahlen 0 bis 255. Man kann natürlich ein Bit aus den acht Bits eines Bytes dazu verwenden, um ein Vorzeichen (+ oder -) zu codieren. Dann können die (dezimalen) Zahlen -128 bis +127 in einem Byte dargestellt werden.
Zwei Bytes werden als Wort (engl. Word), vier Bytes als Langwort (engl. Longword) bezeichnet. Durch die Gruppierung mehrerer Bytes lassen sich auch größere Zahlen darstellen. Mit einem Longword lassen sich beispielsweise die Zahlen von 0 bis 232-1 (=4.294,967.295) darstellen, wenn kein Bit als Vorzeichenbit verwendet wird. Man spricht dann auch von unsigned long integer Zahlen. Mit Vorzeichen (signed long integer) lassen sich die Zahlen von -231 bis +231-1 in einem Langwort darstellen.
Zeichenkodierung
Computersysteme verarbeiten Daten, die in Form von Zeichen eingegeben werden. Da die Rechner intern mit Binärzahlen operieren ist es notwendig, jedem Zeichen - dazu gehören Ziffern, Buchstaben, Sonderzeichen, Symbole, Satzzeichen und nicht druckbare Steuerzeichen - eindeutig eine Zahl zuzuordnen. Diese Zeichenkodierung (character encoding) geschieht mit Hilfe von Zeichensätzen (character sets).
Bis heute bildet der 1963 eingeführte ASCII-Code (American Standard Code of Information Interchange) die Grundlage aktueller Zeichensätze. Jedes Zeichen wird auf einem Byte gespeichert, wobei lediglich 7 Bit verwendet werden. Das höchstwertige Bit des Bytes ist immer 0. Außerdem sind die ersten 33 Zeichen mit Steuerzeichen belegt, weshalb der ASCII-Code lediglich 95 druckbare Zeichen enthält. Die Zuordnung von Nummern und Zeichen ist im Prinzip völlig willkürlich, so hat es beispielsweise keine tiefere Bedeutung, dass dem Buchstaben A gerade die Zahl 65 zugeordnet wurde. Bei der Erstellung beschränkte man sich auf die Zeichen, die in der Geschäftswelt der USA üblich waren. Somit enthält der ASCII-Code zwar die eckigen und geschwungenen Klammern, nicht aber Umlaute oder Buchstaben, die einen Akzent benötigen.
Mit den 128 Zeichen des ASCII-Codes standen nicht genug Zeichen für die Darstellung der wichtigsten Sprachen zur Verfügung, weshalb der Code schon bald erweitert wurde.
Von sinkender Bedeutung ist heute noch
der 1964 von IBM geschaffene EBCDIC-Code
(Extended Binary Coded Decimal Interchange
Code), der insbesondere in Großrechnern verwendet
wird. Es handelt sich dabei um einen 8-Bit-Code, der immer schon auch Zeichen
enthielt, die nur außerhalb der USA Verwendung finden.
Spätere Erweiterungen des Codes haben
immer auch das höchstwertige
Bit mit einbezogen, wodurch mehr Zeichen zur Verfügung stehen. Leider folgten
die verschiedenen Rechnerhersteller bei dieser Erweiterung auf 256 verschiedene
Zeichen keiner einheitlichen Vorgabe. So wies der Zeichensatz des PCs unter
dem Betriebssystem MS DOS für bestimmte Zeichen andere Codes auf als der
Zeichensatz des Apple Macintosh. UNIX-Rechner verstanden wiederum lange Zeit
überhaupt nur die 128 Zeichen des ursprünglichen ASCII-Codes. Erst
im Lauf der Neunziger Jahre des 20. Jahrhunderts einigte man sich schließlich
auf den so genannten ANSI-Code, der unter
anderem von Windows verwendet wurde.
Da es mit den 8-Bit-Codes nicht möglich war, mehr als 256 Zeichen zu kodieren wurde ab dem Jahr 1996 der Zeichensatz Unicode mit dem Ziel entwickelt, möglichst alle bekannten Schriftkulturen und Zeichensysteme abbilden zu können. Der Zeichensatz wird ständig erweitert, derzeit könnte er theoretisch 1.114.112 Zeichen (24 Bit) enthalten, wobei lediglich 136.690 gesetzt sind. Da der sehr umfangreiche Zeichensatz Unicode viele Systeme überlasten würde, hat sich in letzter Zeit vor allem auf Webseiten und im Emailverkehr - als Teil von Unicode - der Zeichensatz UTF-8 etabliert.
Die Unicode- bzw. UTF8-Tabelle enthält offenbar wesentlich mehr Zeichen als mit Hilfe einer gängigen Tastatur erzeugt werden können. Dennoch ist es möglich, solche Zeichen darzustellen, falls die verwendete Schriftart dies zulässt. Textverarbeitungsprogramme unter Windows bieten in der Regel die Möglichkeit, sogenannte Symbole einzugeben, deren hexadezimale Unicodenummer bekannt ist. Eine bequem zu bedienende Tabelle gibt's auf https://unicode-table.com/de. In anderen Editoren sind eine Reihe anderer Tastenkombinationen notwendig. Verhältnismäßig bekannt ist die in Windowsprogrammen mögliche Methode, die dezimale Unicodenummer vierstellig (eventuell mit führenden Nullen) bei gedrückter ALT-Taste am Nummernblock einzugeben.
Das verhältnismäßig neue ẞ, die zum ß gehörige Majuskel, hat die Nummer U+1E9E, ist aber in Word auch über AltGr+Shift+ß erzeugbar.
Der Unicode eignet sich also gut zur Darstellung von Texten. Bildet man eine Kette aus mehreren aufeinanderfolgenden Zeichen, erhält man eine Zeichenkette oder einen String. Da Strings unterschiedlich lang sein können, muss in irgend einer Form auch die Länge der Zeichenkette angegeben werden. Dafür stehen mehrere Methoden zur Verfügung:
Zweierkomplement, negative ganze Zahlen
Für die Darstellung von negativen Zahlen hat sich in der Computertechnik aus praktischen Gründen die so genannte Zweierkomplementdarstellung durchgesetzt: Eine negative ganze Zahl wird dargestellt, indem bei der entsprechenden positiven ganzen Zahl alle Nullen durch Einsen und alle Einsen durch Nullen ersetzt werden und zu dem so gewonnenen Ergebnis 1 addiert wird.
Beispiel (die Abstände nach
jeweils vier Bits dienen lediglich der Lesbarkeit):
positive ganze Zahl 256: | 0000 0001 0000 0000 |
negative ganze Zahl -256: | |
komplementieren 1 addieren Ergebnis |
1111 1110 1111 1111 |
Ist bei vorzeichenbehafteten (signed integer) Ganzzahlen das höchstwertige (d.h. das am weitesten links stehende) Bit gleich 1, handelt es sich um eine negative Zahl. Man beachte, dass die Interpretation eines Bitmusters verschiedene Ergebnisse liefert, je nachdem, ob man das Bitmuster als unsigned integer oder als signed integer betrachtet:
Beispiele:
(a) Wir betrachten die Binärzahl 1111 1111 0000 0000 von
oben:
Interpretation als vorzeichenlose ganze Zahl (unsigned integer): | 1111 1111 0000 0000: 0*20 + 0*21 + 0*22 + 0*23 + 0*24 + 0*25 + 0*26 + 0*27 + 1*28 + 1*29 + 1*210 + 1*211 + 1*212 + 1*213 + 1*214 + 1*215 = 65280 |
Interpretation als vorzeichenbehaftete ganze Zahl (signed integer): Das höchstwertige Bit ist 1, daher ist die Zahl negativ; Umkehrung der Prozedur von oben: | 1111 1111 0000 0000: |
eins subtrahieren komplementieren Ergebnis |
1111 1111 0000 0000 |
Das Ergebnis ist also als -256 zu interpretieren. |
(b) Wenn eine vorzeichenbehaftete Binärzahl aus lauter Einsen besteht, lautet das Ergebnis -1. Zum Nachweis nehmen wir ein Byte an, dessen acht Bits alle auf 1 gesetzt sind. Das Byte ist als vorzeichenbehaftete ganze Zahl zu interpretieren:
Gegeben ist: eins subtrahieren liefert komplementieren: |
1111 1111 1111 1110 0000 0001 |
Die Zweierkomplementdarstellung wird für alle ganzen Zahlen (Bytes, Words, Longwords, ) in gleicher Weise angewandt.
Hexadezimalzahlen
Die Darstellung von Zahlen durch eine Folge von Nullen und Einsen ist sehr umständlich. Bei längeren Bitfolgen verliert man schnell den Überblick. Es liegt daher nahe, mehrere Bits zu einer einzigen Zahl zusammenzufassen. Meist sind dies vier Bits, in früheren Tagen der EDV waren es manchmal auch nur drei Bits. Wenn vier Bits zu einer einzigen Zahl zusammengefasst werden, gelangt man zu einem Zahlensystem mit der Basis 16 (=24). (Bei drei Bits erhält man die Basis 8 = 23.)
Die Basis 16 ist Grundlage des Hexadezimalsystems. In diesem System existieren Ziffern mit den dezimalen Werten von 0 bis 15. Für die Hexadezimalziffern "10" bis "15" müssen neue Symbole gefunden werden. Es werden dafür die Buchstaben A bis F (bzw. a bis f) verwendet. Um eine Hexadezimalzahl zu kennzeichnen, wird ihr oft ein h oder H nachgestellt, damit man sie nicht mit einer Dezimalzahl verwechselt. Eine andere Kennzeichnung von Hexadezimalzahlen ist ein vorangestelltes "0x" (z.B. in der Programmiersprache C).
Mit Hilfe von Hexadezimalzahlen erhält man eine wesentlich kompaktere Schreibweise, wobei die Zahlen überdies rasch in eine Binärzahl übergeführt werden können. Hexadezimalzahlen haben wie Binärzahlen kein Vorzeichen, erst die Interpretation entscheidet, ob sie positiv oder negativ sind.
Beispiele:
vorzeichenlose dezimale Zahl (ganze Zahl): | 65535 |
Binärzahl: | 1111 1111 1111 1111 (die Abstände nach jeweils vier Bits dienen lediglich der Lesbarkeit) |
Hexadezimalzahl: | FFFFh |
Hexadezimalzahl: | 9BE7h |
Binärzahl: | 1001 1011 1110 0111 |
Dezimalzahl: | 9*16³ + 11*16² + 14*16 + 7*1 = 39911 (interpretiert als vorzeichenlose Ganzzahl; eine Interpretation als vorzeichenbehaftete Ganzzahl würde übrigens -25625 ergeben.) |
Little-Endian-Format
Die Prozessoren von Intel verwendet das so genannte Little-Endian-Format. Das bedeutet, dass ein Wort oder Langwort stets mit dem niederwertigen Byte (least significant byte) beginnt und mit dem höchstwertigen (most significant byte) endet. Das bedeutet, dass ein höherwertigeres Byte einer Größe, die aus mehr als einem Byte besteht, an einer höheren Adresse gespeichert wird als ein niederwertigeres Byte.
Wenn man ein Wort oder Langwort aufschreibt, setzt man das höchstwertige Byte an die am weitesten links stehende Position und das niederwertigste Byte an die am weitesten rechts stehende Stelle. Wenn man Speicheradressen niederschreibt, steigen sie aber üblicherweise von links nach rechts an. Daher scheinen die Speicherinhalte von Little-Endian-Prozessoren im Vergleich zu ihrer "erwarteten" Reihenfolge in Worten oder Langworten byteweise vertauscht zu sein. Das betrifft auch die Inhalte von Dateien, weil sie oft ein genaues Abbild von Speicherinhalten enthalten.
Ein Wort mit dem (hexadezimalen) Wert 12DEh wird also im Memory als DE12h aufscheinen, ein Langwort mit dem Inhalt 12345678h wird abgelegt als 78563412h. Das ist bei der Untersuchung von Speicherabbildern ("Hex-Dumps") zu berücksichtigen.
Big-Endian-Format
Im Gegensatz dazu beginnt ein Wort oder Langwort im Big-Endian-Format immer mit dem most significant byte. Prozessoren von Motorola verwenden dieses Format.
Wenn binäre Daten zwischen Rechnern unterschiedlicher Architektur ausgetauscht werden sollen (beispielsweise mit Hilfe des Programms FTP), muss der Umstand, dass zusammen gehörige Bytes in unterschiedlicher Reihenfolge gespeichert sein können, berücksichtigt werden. Es gibt dazu eine eigens definierte Network Byte Order. Die Network-Byte-Order stimmt mit dem Big-Endian-Format überein.