Teil 11 „The character set“

Der C-Kurs
Antworten
Benutzeravatar
bodo
User
Beiträge: 319
Registriert: 14.02.2007, 17:21
Kontaktdaten:

Teil 11 „The character set“

Beitrag von bodo » 30.10.2010, 16:40

C für BASIC-Programmierer

Arbeiten mit dem z88dk Cross-Compiler

von Jens Sommerfeld und Bodo Wenzel

Teil 11

Nachdem wir im letzten Teil bereits die Verzweigung als Struktur kennengelernt haben, könnten wir gleich z.B. mit den Schleifen weitermachen. Aber Mister Vickers betitelte sein Kapitel 11 mit „The character set“. Daher geht es in diesem Teil um Zeichen und ihren Datentyp „char“ und dessen ganzzahlige Natur; die Schleifen kommen als nächstes, versprochen!

Auch C hat einen Zeichensatz – und nicht nur einen, sondern gleich zwei! Der ISO-Standard definiert zunächst einen Zeichensatz für den Quelltext des Programms, der vom Compiler verstanden werden muss. Der andere Zeichensatz wird zur Darstellung der Ein- und Ausgabe benutzt... Ihr seht so fragend aus... Vielleicht muss ich nochmal erklären, was ein Zeichensatz ist.

Eigentlich ist es ganz einfach: ein Zeichensatz definiert, mit welchem Kode welches Zeichen kodiert wird. Der Kode ist dabei eine Ganzzahl, beim Zeddy 6 Bits breit (Das siebte Bit kennzeichnet Bytes, die bei der Bildausgabe als Maschinenbefehl auszuführen sind; dazu gehört auch „N/L“ (Newline), das in Wirklichkeit ein „HALT“ ist... Aber dessen Erklärung führe hier zu weit. Das achte Bit dient der Invertierung des Zeichens.), beim Original-ASCII 7 Bits breit, bei Unicode 16 Bits, und bei UTF-8 variabel breit.

In C werden Zeichen mit dem Datentyp „char“ deklariert, das haben wir schon gehabt. Dieser Datentyp ist garantiert 8 Bit breit und kann deshalb mit Vorzeichen die Werte -128 bis +127 oder ohne Vorzeichen die Werte 0 bis 255 annehmen. Probieren wie das einmal aus:

Code: Alles auswählen

#include <stdio.h>

int main(void) {
    char          zeichen;
    signed char   mit_vorzeichen;
    unsigned char ohne_vorzeichen;

    zeichen = 0;
    mit_vorzeichen = zeichen;
    ohne_vorzeichen = zeichen;
    printf("%d %d %d\n", zeichen, mit_vorzeichen, ohne_vorzeichen);

    zeichen = -1;
    mit_vorzeichen = zeichen;
    ohne_vorzeichen = zeichen;
    printf("%d %d %d\n", zeichen, mit_vorzeichen, ohne_vorzeichen);

    zeichen = 81;
    mit_vorzeichen = zeichen;
    ohne_vorzeichen = zeichen;
    printf("%d %d %d\n", zeichen, mit_vorzeichen, ohne_vorzeichen);

    zeichen = -81;
    mit_vorzeichen = zeichen;
    ohne_vorzeichen = zeichen;
    printf("%d %d %d\n", zeichen, mit_vorzeichen, ohne_vorzeichen);

    zeichen = -128;
    mit_vorzeichen = zeichen;
    ohne_vorzeichen = zeichen;
    printf("%d %d %d\n", zeichen, mit_vorzeichen, ohne_vorzeichen);

    zeichen = +127;
    mit_vorzeichen = zeichen;
    ohne_vorzeichen = zeichen;
    printf("%d %d %d\n", zeichen, mit_vorzeichen, ohne_vorzeichen);

    return 0;
}
Ihr seht, dass das Weglassen von „signed“ oder „unsigned“ beim z88dk automatisch „signed“ ergibt. Dies ist häufig so, aber nicht immer! Der Datentyp „char“ kann bei einigen C-Compilern auch per Default als „unsigned“ kommen.

Natürlich könnt ihr auch mit diesen Variablen rechnen, alles was mit einem Integer dieser „Breite“ halt so geht. Der Typ „unsigned char“ ist daher auch der bevorzugte Typ, wenn ihr Byte-Variablen anlegt.

Wenn wir jetzt darüber nachdenken, welche Zeichensätze beim z88dk gelten, kommen wir erst einmal darauf, dass der Quelltext in ASCII kodiert ist. Das ist auch nicht verwunderlich, weil der Compiler ja auf heutigen normalen Betriebssystemen läuft, die einfache Texte in ASCII kodieren.

Um herauszubekommen, welcher Zeichensatz zur Laufzeit gilt, schreiben wir folgendes kleines Testprogramm:

Code: Alles auswählen

#include <stdio.h>

int main(void) {
    char zeichen;

    zeichen = '0';
    printf("%d = %c\n", zeichen, zeichen);

    zeichen = '9';
    printf("%d = %c\n", zeichen, zeichen);

    zeichen = 'A';
    printf("%d = %c\n", zeichen, zeichen);

    zeichen = 'Z';
    printf("%d = %c\n", zeichen, zeichen);

    return 0;
}
Und das Ergebnis: offenbar wird auch hier der ASCII benutzt. Die Ausgabe wird dann von der Routine zur Zeichenausgabe automatisch in den ZX81-Zeichensatz umkodiert; dies ist auch so dokumentiert, und das Verhalten kann geändert werden. Das ist aber Stoff für einen späteren Teil.

Natürlich möchten wir auch die Sonderzeichen bei der Definition von Zeichen- und Zeichenkettenkonstanten (den Apostroph, das Anführungszeichen und den Rückwärtsschrägstrich) angeben können (Auch wenn diese Zeichen beim ZX81 fast alle nicht vorhanden sind: wir haben ja nicht nur mit diesem PC zu tun, sondern werden vielleicht auch 'mal auf einem anderen arbeiten.). Dies wird möglich durch ein spezielles Fluchtzeichen (englisch: escape), das als Umschaltzeichen dient; ihr kennt es schon, weil ihr damit das Zeilenendezeichen bei den Ausgabetexten kodiert habt. Es ist – tataa – der Rückwärtsschrägstrich! Die folgende Tabelle zeigt die möglichen Kombinationen, die zu jeweils einem Zeichen führen:
tabelle.gif
tabelle.gif (22.74 KiB) 2394 mal betrachtet
Anmerkung 31: Also, eigentlich muss hier der Wert 10 stehen, aber das z88dk hat das offenbar mit '\r' vertauscht!
Anemrkung 32: Also, eigentlich muss hier der Wert 13 stehen, aber das z88dk hat das offenbar mit '\n' vertauscht!

Wozu sind wohl die drei Kombinationen \?, \', und \“? Bei den beiden letzten ist die Sache logisch, sie werden in Zeichenkonstanten und Zeichenkettenkonstanten benutzt, die ja den Apostroph und das Anführungszeichen als Begrenzung verwenden. Beim Fragezeichen gibt es aber ebensolche in den Gesichtern! Nun, das stammt von einem unglücklich gewählten und sehr unbekannten „Feature“, den Drei -Zeichen-Folgen. Mit diesen können bestimmte Zeichen auch auf Systemen mit eingeschränkten Quelltextzeichensätzen angegeben werden... Aber damit will ich euch nicht belasten, das benutzt eh' niemand!

Interessant an der Tabelle sind für Bitfrickler die letzten beiden Zeilen. Ihr könnt beliebige Zeichen in oktal oder hexadezimal kodieren. Das ist sehr praktisch für Sonderzeichen.

So, wenn wir jetzt das Originalkapitel Revue passieren lassen, stellen wir fest, dass in C die Funktionen CODE und CHR$ garnicht benötigt werden. Ein Wert vom Datentyp char ist immer gleichzeitig Zeichen und Ganzzahl, und seine Interpretation hängt nur vom Zusammenhang ab. Ihr werdet merken, dass das auch kein Problem ist.

Wie wir an die (Low-Resolution-)Grafikzeichen kommen, wird Stoff für einen anderen Teil sein. Bis dahin viel Spaß mit dem Bisherigen!
B0D0: Real programmers do it in hex.

Antworten