Die Suche ergab 233 Treffer

von bbock
08.03.2023, 13:20
Forum: Programmierung
Thema: Programmieren in C mit dem z88dk
Antworten: 20
Zugriffe: 9467

12. Strukturen

Strukturen sind Zusammenfassungen von mehreren Variablen, die in der Regel - im Gegensatz zu Arrays - von verschiedenem Datentyp sind. Sie beschreiben oft Objekte mit ihren interessierenden Eigenschaften. So könnte man z.B. ein Buch über Titel, Autor, Thema und die Buch-ID definieren:

Code: Alles auswählen

struct Book {
    char title[50];
    char author[50];
    char subject[100];
    int  book_id;
};
Wir wollen zwei Bücher initialisieren und ausgeben. Zunächst die Variablendeklarationen:

Code: Alles auswählen

struct Book book1;
struct Book book2;
Dann befüllen wir die zur Struktur gehörigen Variablen (Member-Variablen) der beiden Bücher. Schließlich geben wir sie auf dem Bildschirm aus; dafür verwenden wir eine eigene Funktion. Alles zusammen könnte etwa so aussehen:

Code: Alles auswählen

#include <stdio.h>
#include <string.h>

// A book.
struct Book {
    char title[50];
    char author[50];
    char subject[100];
    int  book_id;
};

/* Print a book.
   Parameter: book  pointer to the book to print
*/
void printBook(struct Book *book) {
    printf( "title   : %s\n", book->title);
    printf( "author  : %s\n", book->author);
    printf( "subject : %s\n", book->subject);
    printf( "book_id : %d\n", book->book_id);
}

/* Main.
*/
int main(void) {
    struct Book book1;
    struct Book book2;

    /* book 1 specification */
    strcpy(book1.title, "Programmieren in C");
    strcpy(book1.author, "Kernighan & Ritchie"); 
    strcpy(book1.subject, "Programmierlehrbuch");
    book1.book_id = 5407;

    /* book 2 specification */
    strcpy(book2.title, "Telecom Billing");
    strcpy(book2.author, "Zara Ali");
    strcpy(book2.subject, "Telecom Billing Tutorial");
    book2.book_id = 5700;
    
    puts("*** Book 1 ***");
    printBook(&book1);
    
    puts("\n*** Book 2 ***");
    printBook(&book2);
    
    return 0;
}
Wie wir sehen, greift man auf die Member-Variablen eines struct mit Hilfe des Punkt-Operators zu, beispielsweise book1.title. Wollen wir aber über einen Pointer auf einen struct auf Member-Variablen zugreifen, dann ist der Pfeil-Operator dafür zuständig, z.B. book->title. Man könnte auch (*book).title schreiben, aber das ist umständlicher. Der Pfeil erinnert sofort an "Zeiger", daher ist diese Schreibweise intuitiver.

Beim Arbeiten mit Strukturen werden wir häufig Pointer verwenden, insbesondere, wenn wir struct-Variablen als Argument einer Funktion übergeben oder als Funktionsergebnis zurückliefern wollen.
von bbock
07.03.2023, 21:15
Forum: Programmierung
Thema: Programmieren in C mit dem z88dk
Antworten: 20
Zugriffe: 9467

11. Aufzählungstypen

In C gibt es den keinen Boolean-Datentyp, obwohl man ihn gut gebrauchen könnte. In Bedingungsausdrücken gilt jeder Wert ungleich 0 als true, die 0 als false. Daher kann man z.B. eine char-Variable anstelle einer Boolean-Variablen verwenden und ihren Wert auf 1 (bzw. '\1') für true oder 0 (bzw. '\0') für false setzen. In der Taschenrechner-Übung haben wir das bereits bei der Variablen continueLoop getan.

Man kann aber eine Boolean-Variable in C nachbilden, indem man den Datentyp Boolean als eine Aufzählung der beiden Werte true und false auffasst. Das wird unser erster Aufzählungstyp (englisch enumeration type):

Code: Alles auswählen

enum bool {false, true};
Mit dieser Deklaration können wir Variablen vom Typ bool anlegen, denen man ausschließlich die beiden Werte true und false zuweisen kann:

Code: Alles auswählen

enum bool adult;
...
if (age >= 18)
    adult = true;
else
    adult = false;
In der enum haben wir nicht zufällig zuerst false und dann true notiert: intern werden enums als Zahlwerte repräsentiert, mit 0 beginnend. Dadurch entspricht false der 0 und true der 1. Weil C die 0-Werte als false und Werte ungleich 0 als true interpretiert, passt das.

Aufzählungstypen kann man für alle Variablen verwenden, die nur bestimmte, festgelegte Werte annehmen sollen. Eine Variablentyp für die Planeten unseres Sonnensystems könnte z.B. so deklariert werden:

Code: Alles auswählen

enum planet {Merkur, Venus, Erde, Mars, Jupiter, Saturn, Uranus, Neptun};
Ein kleines Beispielprogramm dazu:

Code: Alles auswählen

#include <stdio.h>

int main(void) {
    enum planet {Merkur, Venus, Erde, Mars, Jupiter, Saturn, Uranus, Neptun};
    enum planet myPlanet;
    char *planetName;
    float density;
    
    for (myPlanet = Merkur; myPlanet <= Neptun; ++myPlanet) {
        switch (myPlanet) {
            case Merkur:
                planetName = "Merkur";
                density = 5.427;
                break;
            case Venus:
                planetName = "Venus";
                density = 5.243;
                break;
            case Erde:
                planetName = "Erde";
                density = 5.514;
                break;
            case Mars:
                planetName = "Mars";
                density = 3.933;
                break;
            case Jupiter:
                planetName = "Jupiter";
                density = 1.326;
                break;
            case Saturn:
                planetName = "Saturn";
                density = 0.687;
                break;
            case Uranus:
                planetName = "Uranus";
                density = 1.271;
                break;
            case Neptun:
                planetName = "Neptun";
                density = 1.638;
                break;
        }
        
        printf("Die mittlere Dichte des Planeten %s betraegt %5.3f g/cm^3\n", planetName, density);
    }
    
    return 0;
}
Man beachte, dass die symbolischen Namen aus der enum-Deklaration nicht direkt mit printf als Text ausgegeben werden können. Wenn der Compiler den Code erzeugt hat, dann existieren diese Namen nicht mehr, sondern nur noch die zugehörigen Zahlwerte. Daher weisen wir der Variablen planetName die passenden String-Konstanten zu.

Die Formatangaben in printf bedeuten:
%s gibt einen String aus; die korrespondierende Variable muss ein char-Pointer sein.
%5.3f gibt eine Fließkommazahl mit insgesamt fünf Zeichen (inkl. Dezimalpunkt) und drei Nachkommastellen aus.
von bbock
05.03.2023, 14:30
Forum: Hardware
Thema: PCW WiFi Modem
Antworten: 35
Zugriffe: 18049

Re: PCW WiFi Modem

bbock hat geschrieben: 26.02.2023, 17:00 ... Das Flashen war bei dir erfolgreich?
Daher meine Frage...
von bbock
03.03.2023, 12:02
Forum: Programmierung
Thema: Programmieren in C mit dem z88dk
Antworten: 20
Zugriffe: 9467

Übung

Das bis hierher Gelernte wollen wir in einer kleinen Übung vertiefen: wir programmieren einen einfachen Taschenrechner. Damit das Ganze überschaubar bleibt, sollen nur die Grundrechenarten unterstützt werden - ohne Punkt- vor Strichrechnung.

Wir beginnen mit der main-Funktion mit einigen Variablendeklarationen und der Ausgabe einer Überschrift. Endlich bekommt die Funktion auch einen Kommentar:

Code: Alles auswählen

#include <stdio.h>
#include <string.h>

/* Main.

   Returns an error code (== 0: ok, != 0: error).
*/
int main(void)
{
    float fInput, fAkku;
    char op, currentOp;
    char continueLoop;
    char formatted[20];
    
    printf("Mini-Taschenrechner\n\n");
Die Variable fInput verwenden wir für die Benutzereingabe, mit fAkku wird gerechnet. Dann brauchen wir noch zwei Variablen für den Operator, eine Hilfsvariable für die Schleife und einen String (d.h. einen char-Array) für die formatierte Ausgabe von Zahlen.

Dann folgt ein wenig Initialisierung:

Code: Alles auswählen

    op = '\0';
    fAkku = 0;
    continueLoop = 1;
Und schließlich die Schleife und der Rest der main-Funktion:

Code: Alles auswählen

    do {
        printf("Zahl     : ");
        scanf("%f", &fInput);
        
        printf("Operation: ");
        currentOp = getchar();
        currentOp = getchar();
        
        switch (op) {
            case '\0':
                fAkku = fInput;
                break;
            case '+':
                fAkku = fAkku + fInput;
                break;
            case '-':
                fAkku = fAkku - fInput;
                break;
            case '*':
                fAkku = fAkku * fInput;
                break;
            case '/':
                fAkku = fAkku / fInput;
                break;
            default:
                continueLoop = 0;
        }
        
        formatNumber(fAkku, formatted);
        printf("    %s\n", formatted);
        op = currentOp;
    } while (currentOp != '=' && continueLoop);
    
    return 0;
}
In der Schleife gibt der Benutzer eine Zahl ein, die via scanf als float-Wert eingelesen wird (daher der Format-String "%f"). Der Wert landet in der Variablen fInput, deren Adresse wir scanf als zweites Argument übergeben.

Dann fordern wir den Benutzer auf einen Operator einzugeben. Den lesen wir mit getchar ein, dann muss der Operator nicht mit der Enter-Taste bestätigt werden. Es sieht hier so aus, als würden wir den Operator zweimal einlesen, aber der erste getchar-Aufruf räumt nur etwas "Müll" aus dem Tastaturpuffer, den scanf zurückgelassen hat. Der zweite getchar-Aufruf ist der, der uns den Operator liefert.

In der switch-Anweisung sieht man, welche Operatoren erlaubt sind: +, -, *, / und =. Der case-Zweig mit dem '\0' wird nur beim ersten Durchlauf erreicht, weil op mit '\0' initialisiert wurde. Im ersten Durchlauf gibt es auch noch nichts zu rechnen; wir speichern nur die erste Zahl in fAkku.

In jedem weiteren Durchlauf wird fAkku abhängig vom Operator mit dem nächsten Eingabewert verknüpft. Wird ein ungültiger Operator eingegeben, dann bricht die Schleife ab, indem continueLoop auf 0 gesetzt wird (continueLoop wurde mit 1 initialisiert). Aber continueLoop ist doch eine char-Variable, wie kann sie dann auf eine Zahl wie 0 oder 1 gesetzt werden? Der C-Compiler ist hier recht tolerant; wenn man einer char-Variablen einen Zahlwert zuweist, dann nimmt er das klaglos als ASCII-Code an, solange der Wertebereich nicht überschritten wird. Für vorzeichenbehaftete char ist das -128 bis +127, für unsigned char 0 bis 255. Wir verwenden hier den char als verkappte Boolean-Variable.

Die while-Bedingung gibt an, wann die Schleife weiterlaufen soll. Ist der aktuelle Operand ein '=', dann wird die Schleife regulär beendet.

Eine Sache fehlt noch: Die Ausgabe des jeweiligen Zwischenergebnisses erfolgt nicht direkt durch printf("%f", fAkku);, sondern es wird die Funktion formatNumber aufgerufen. Die macht die Zahl etwas hübscher, indem sie sie in einen String umwandelt.

Schauen wir uns die Funktion mal genauer an. Sie muss vor der main-Funktion, aber nach den #include-Anweisungen eingefügt werden:

Code: Alles auswählen

/* Formats a float by cutting off the zeroes at the end.
   If there are only zeroes after the decimal point, then
   the decimal point is cut off, too.
   
   Parameters: f          the float value to format
               formatted  a character buffer to hold the float as string
*/
void formatNumber(float f, char *formatted) {
    char *s;
    
    sprintf(formatted, "%f", f);
    s = formatted + strlen(formatted);
    --s;
    
    while (s != formatted) {
        if (*s != '0') {
            if (*s != '.') {
                ++s;
            }
            *s = '\0';
            break;
        }
        --s;
    }
}
Wir übergeben ihr den zu formatierenden float-Wert und einen Pointer (die Adresse) auf eine Zeichenkette, in die die formatierte Zahl abgelegt werden soll. Das ist unser char-Array formatted.

Zu Beginn wird die Zahl f in eine Zeichenkette umgewandelt. Dies geschieht mit der Funktion sprintf, die genauso funktioniert wie printf, nur landet die Ausgabe nicht auf dem Bildschirm, sondern in der String-Variablen, die als erstes Argument übergeben wird. Danach ist alles wie bei printf: der Format-String, dann die Variablen, die die Format-Platzhalter mit Werten füllen.

Die folgende Schleife schneidet alle unnötigen Nullen am Ende der Zahl, nach dem Dezimalpunkt ab. Der String wird von rechts beginnend Zeichen für Zeichen durchlaufen, bis ein Zeichen ungleich '0' gefunden wird. Das folgende Zeichen wird mit dem String-Abschlusszeichen '\0' überschrieben, wodurch der String verkürzt wird. Eine Sonderbehandlung gibt es noch für den Dezimalpunkt: Damit eine Zahl wie 2.000000 nicht zu 2. resultiert, wird hier nicht das folgende Zeichen, sondern der Dezimalpunkt selbst mit dem Abschlusszeichen überschrieben. Das Ergebnis ist dann einfach eine 2 ohne den verwaisten Dezimalpunkt.
von bbock
02.03.2023, 07:30
Forum: Programmierung
Thema: Fragen und Antworten zum C-Kurs
Antworten: 108
Zugriffe: 33930

Re: Fragen und Antworten zum C-Kurs

Ja, die Pointer haben's in sich; deswegen sind sie in manchen modernen Programmiersprachen wie Java - ein C-Abkömmling - gar nicht erst verfügbar. Man kann viel Unheil mit ihnen anrichten. Aber sie sind eben auch ein mächtiges Werkzeug.

Die Umwandlung der Kommentare via Präprozessor ist meines Wissens nicht möglich. Man könnte aber ein (gar nicht mal so kompliziertes) Programm schreiben, das nach "//" sucht, durch "/*" ersetzt, dann den darauffolgenden Zeilenumbruch sucht (Achtung: betriebssystemabhängig!) und vor diesem ein "*/" einfügt. Das können wir mal aufgreifen, wenn wir zur Dateiverarbeitung kommen.
von bbock
28.02.2023, 18:19
Forum: Hardware
Thema: PCW WiFi Modem
Antworten: 35
Zugriffe: 18049

Re: PCW WiFi Modem

Paul hat geschrieben: 27.02.2023, 16:29 ...
CP/M Box Version 1.9.2 benutze ich. Und normalerweise die Enter Taste am Ziffernblock.

Test3.zip
Hallo Paul,

die 64-Bit-Version, nehme ich an? Egal, ich habe die 64- und die 32-Bit-Version von 1.9.2 und 1.9.1 ausprobiert. Bei mir tritt der Effekt nicht auf. Vielleicht solltest du mal Screenshots deiner Einstellungen posten; vielleicht ist es ja eine besondere Einstellung.
von bbock
28.02.2023, 18:03
Forum: Hardware
Thema: PCW WiFi Modem
Antworten: 35
Zugriffe: 18049

Re: PCW WiFi Modem

In Helmarshausen werde ich auch sein - falls mir nicht der Himmel auf den Kopf fällt. :)
Ich könnte auch eine Joyce mitbringen und das Modem testen, aber wenn hardware-seitig etwas zu reparieren ist, dann sollte das jemand übernehmen, der das besser kann als ich.
von bbock
28.02.2023, 12:46
Forum: Verschiedenes
Thema: Ab 1.3.2023: eBay-Verkäufe kostenlos für private Verkäufer
Antworten: 0
Zugriffe: 3577

Ab 1.3.2023: eBay-Verkäufe kostenlos für private Verkäufer

Ab dem 1. März diesen Jahres entfallen bei eBay Deutschland die Angebotsgebühren und die Verkaufprovisionen für private Verkäufer:

https://www.heise.de/news/Ebay-Deutschl ... 29516.html
von bbock
26.02.2023, 17:43
Forum: Programmierung
Thema: Programmieren in C mit dem z88dk
Antworten: 20
Zugriffe: 9467

10.3 String

Wie bereits erwähnt, gibt es in C keinen Datentyp "String". Wenn man von C-Strings spricht, dann meint man Arrays von Zeichen, die mit einem Null-Zeichen '\0' abgeschlossen ("terminiert") werden. Diese Art von Zeichenketten ist sehr verbreitet; in C gibt es eine ganze Bibliothek von String-Funktionen, die man #include <string.h> einbinden kann.

Da Strings eigentlich Arrays von char-Werten sind, gelten für sie auch die bereits genannten Regeln für Pointer und Arrays. Besonders häufig trifft man String-Literale an, also String-Konstanten, die in Gänsefüßchen geschrieben werden, z.B.:

"Dies ist ein String"

Wie wird ein String im Speicher abgelegt? Nehmen wir beispielsweisen den String "hallo": Er besteht aus fünf Zeichen, hat also eine Länge von 5. Dazu kommt aber noch das Abschlusszeichen, so dass im Speicher tatsächlich sechs Bytes belegt werden. Ohne das Abschlusszeichen wüsste eine Funktion wie printf oder puts nicht, wann sie mit dem Ausgeben von Zeichen aufhören soll. Diese Funktionen bekommen die Adresse des Strings übergeben und geben Zeichen für Zeichen aus, wobei sie die Adresse immer weiter hochzählen, bis sie schließlich auf eine 0 stoßen. Dann ist der String zuende und die Ausgabe hört auf.

Code: Alles auswählen

Adresse    3000 3001 3002 3003 3004 3005
ASCII-Wert  104   97  108  108  111    0
Zeichen       h    a    l    l    o   \0
Beginnen wir mit einem Beispielprogramm. Hier wird ein String als Array angelegt:

Code: Alles auswählen

#include <stdio.h>

int main(void) {
    char s[6];
    
    s[0] = 'h';
    s[1] = 'a';
    s[2] = 'l';
    s[3] = 'l';
    s[4] = 'o';
    s[5] = '\0'; // Terminierung
    
    puts(s);     // String ausgeben
}
Hier weisen wir einem char-Pointer eine String-Konstante zu - auch das ist ein String:

Code: Alles auswählen

#include <stdio.h>

int main(void) {
    char *s;
    
    s = "hallo"; // Terminierung implizit enthalten
    puts(s);     // String ausgeben
}
Nun wollen wir aus dem "hallo" ein "halli" machen; hier die Array-Variante:

Code: Alles auswählen

#include <stdio.h>

int main(void) {
    char s[6];
    
    s[0] = 'h';
    s[1] = 'a';
    s[2] = 'l';
    s[3] = 'l';
    s[4] = 'o';
    s[5] = '\0'; // Terminierung
    
    puts(s);     // String ausgeben
    
    s[4] = 'i';  // String aendern
    puts(s);     // geaenderten String ausgeben
}
Nun die Variante mit der String-Konstanten:

Code: Alles auswählen

#include <stdio.h>
#include <string.h>

int main(void) {
    char *s1;
    char s2[6];
    char *cp;
    
    s1 = "hallo";   // String-Konstante
    puts(s1);       // String ausgeben
    strcpy(s2, s1); // String von s1 nach s2 kopieren
    cp = s2;        // cp zeigt auf s2[0]
    cp += 4;        // cp zeigt auf s2[4]
    *cp = 'i';      // String aendern
    puts(s2);       // geaenderten String ausgeben
}
Hier ändern wir nicht den String in der String-Konstanten - das sollten wir niemals tun! - sondern wir kopieren den String in eine lokale Variable mit genügend Speicher und ändern die String-Kopie. Diese ändern wir etwas umständlich über einen Hilfs-Pointer. Man hätte natürlich auch genausogut s[4] = 'i'; schreiben können; das wäre drei Zeilen kürzer gewesen.

Das folgende Programm zeigt einige Beispiele zur Verwendung von Funktionen aus string.h:

Code: Alles auswählen

#include <stdio.h>
#include <string.h>

int main(void)
{
    char s[255];
    char *cp;
    int len;
    int i;

    // copy a string
    strcpy(s, "12345678");
    puts(s);
    
    // concatenate strings
    strcat(s, "9ABCDEF");
    puts(s);
    
    // string length
    len = strlen(s);
    printf("L%cnge: %d\n", 123, len);
    
    // compare strings
    i = strcmp("abc", "bcd");
    printf("strcmp Ergebnis: %d\n", i);
    if (i < 0) {
        puts("abc < bcd");
    }
    else if (i > 0) {
        puts("abc > bcd");
    }
    else { // i == 0
        puts("abc == bcd");
    }
    
    // find character in string
    cp = strchr(s, 'A');
    printf("A gefunden; String ab dem A: %s\n", cp);
    
    // find substring in string
    cp = strstr(s, "567");
    printf("567 gefunden; String ab 567: %s\n", cp);
    
    getchar();
    
    return 0;
}
Bei der Ausgabe von "Länge" nach strlen wurde ein Trick angewandt um den Umlaut korrekt auszugeben: Da die Zeichensätze auf dem PC und der Joyce voneinander abweichen, wurde im printf an der Stelle, wo das 'ä' erscheinen soll, ein %c geschrieben, d.h. an dieser Stelle wird ein char eingefügt, das als dem printf in der Parameterliste mitgegeben werden muss. Daher die 123, denn das ist der Code für ein 'ä'.
von bbock
26.02.2023, 17:14
Forum: Hardware
Thema: PCW WiFi Modem
Antworten: 35
Zugriffe: 18049

Re: PCW WiFi Modem

Paul hat geschrieben: 25.02.2023, 09:36 Erstmal: Das CPM lädt problemlos und mit deutscher Tastatur ist auch die Belegung sehr gut.
Kurze Frage:
wenn ich in der CPMBox zu langsam bin und meinen Finger einen Hauch zu langsam von der Enter Taste nehme bekomme ich viele, sehr viele, Enter von CPM, also statt DIR zeigt er mir nach dem DIR ganz viele A> untereinander an und der Verzeichnisinhalt ist schon wieder weg. So schnell kann ich gar nicht lesen :shock:
Gibt es da etwas das ich einstellen kann damit ich nicht immer höllisch aufpassen muss beim betätigen der Entertaste?
Das gewöhnliche autorepeat funktioniert problemlos, nur Enter ist zu schnell.
Schönes Wochenende.
Also, das mit der zu schnellen Tastenwiederholung kenne ich nur, wenn ich den "Fast Mode" einschalte (mit F11 oder mit der "Vorwärtsspulen"-Schaltfläche). Das betrifft dann alle Tasten. Die Return-Taste macht bei mir gar keine Tastenwiederholung, die Enter-Taste vom Ziffernblock schon.

Welche CP/M Box Version verwendest du denn?