Die Suche ergab 238 Treffer

von bbock
18.03.2023, 21:18
Forum: Programmierung
Thema: Fragen und Antworten zum C-Kurs
Antworten: 108
Zugriffe: 501420

Re: Fragen und Antworten zum C-Kurs

Paul hat geschrieben: 18.03.2023, 17:41...
Was mache ich falsch?
Du hast die Mathematik-Library nicht eingebunden. Ändere deinen Compiler-Aufruf in:

zcc +cpm -subtype=pcw80 -lm -create-app calc.c -o calc.com

Damit sollte es funktionieren. Beschrieben ist das in Kapitel 5. Vielleicht sollte ich bei jedem Beispiel den zcc-Aufruf angeben, damit es keine Missverständnisse gibt. Zu den zcc-Parametern hatte ich sowieso noch einen Artikel geplant. Die Parameter sind nicht wirklich intuitiv, und manchmal auch etwas merkwürdig. Z.B. braucht man bei der Joyce (Platform PCW) die Option -create-app, wenn man etwas mit Grafik machen möchte, wodurch man auch ein Disketten-Image erstellt, welches man aber gar nicht unbedingt benötigt. Bei anderen Plattformen braucht man das nicht...

Das floatpack ist ebenfalls in der Mathe-Lib enthalten; ändere deinen zcc-Aufruf entsprechend.
von bbock
17.03.2023, 21:49
Forum: Programmierung
Thema: Fragen und Antworten zum C-Kurs
Antworten: 108
Zugriffe: 501420

Re: Fragen und Antworten zum C-Kurs

schombi hat geschrieben: 17.03.2023, 06:46 ...
Mal sehen, wie sich ein eher unterdurchschnittlicher Basicprogrammierer hier schlaegt :)
...
Das wird schon. Scheue dich nicht Fragen zu stellen, wenn du irgendwo nicht weiter kommst. Die Hauptsache ist, dass man am Ball bleibt. :)
von bbock
17.03.2023, 21:44
Forum: Programmierung
Thema: Programmieren in C mit dem z88dk
Antworten: 20
Zugriffe: 103263

13. Typ-Definitionen

Die einfachste Form der Typ-Definition in C ist die Umbenennung eines bestehenden Typs, wie z.B.

Code: Alles auswählen

typedef int LENGTH;
Das kann die Lesbarkeit fördern, indem man z.B. Variablen wie folgt definiert:

Code: Alles auswählen

LENGTH len, maxlen;
LENGTH *lengths[];
Oder man verwendet Abkürzungen wie

Code: Alles auswählen

typedef unsigned int UINT;
Auf unserem 8-Bitter hätte eine Variable vom Typ UINT dann einen Wertebereich von 0 bis 65535 - genau wie ein unsigned int, nur ist es mit dem typedef halt kürzer. Manch einer mag argumentieren, dass man damit nur die Sprache verwässert und Verwirrung stiftet. Es gibt natürlich auch sinnvollere Anwendungen von typedef, wie folgende Typvereinbarung für einen Knoten in einem binären Baum zeigt:

Code: Alles auswählen

typedef struct tnode {
    char *word;
    int count;
    struct tnode *left;
    struct tnode *right;
} TREENODE, *TREEPTR;
Hier wurden gleich zwei Namen festgelegt, TREENODE für die Struktur und TREEPRT für einen Zeiger auf einen TREENODE. Eine Knoten-Variable legt man z.B. folgendermaßen fest:

Code: Alles auswählen

TREENODE t;
Man hätte die Variable auch ohne typedef als struct tnode t; anlegen können, aber es ist durchaus angenehm, dass das Schlüsselwort struct bei Verwendung von typedef entfällt. Schauen wir uns an, wie das Programm aus Kapitel 12 bei Verwendung von typedef aussieht - wir speichern es als struct2.c:

Code: Alles auswählen

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

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

/* Print a book.
   Parameter: book  pointer to the book to print
*/
void printBook(BookPtr 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) {
    Book book1;
    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;
}
Hier wurde auch gleich ein Pointer auf ein Book definiert, so dass die Funktion für die Ausgabe eines Buchs mit void printBook(BookPtr book) beginnt. Ohne den Pointer-Typ hätte man auch void printBook(Book *book) schreiben können.

Das Programm kompilieren wir zur Abwechslung mit

Code: Alles auswählen

zcc +cpm -subtype=pcw80 -lndos -create-app struct2.c -o struct2.com
Wir kennen bereits das Compiler-Frontend zcc mit den Optionen +cpm, -subtype=pcw80 und -create-app für die Schneider Joyce. Die Option -lndos sorgt für eine kleinere ausführbare com-Datei, weil der Code für Dateizugriffsfunktionen weggelassen wird. Das können wir hier tun, weil wir keine Dateioperationen verwenden.
von bbock
12.03.2023, 21:42
Forum: Programmierung
Thema: Fragen und Antworten zum C-Kurs
Antworten: 108
Zugriffe: 501420

Re: Fragen und Antworten zum C-Kurs

kurt hat geschrieben: 12.03.2023, 10:26 Eine Frage zu den Aufzählungstype und der Art wie TRUE u. FALSE als logisches Flag erzeugt werden. Man sieht auch häufig die Zu.-Fuß-Methode:

Code: Alles auswählen

TRUE  = 0;
FALSE = ~TRUE;
Ich nehme mal an, daß das persönlicher Programmierstil ist. Mir würde dieser Weg auch mehr zusprechen, da der logische Zusammenhang klarer ausformuliert ist (bin halt mehr Hardware-Fritze, FALSE ist nun mal das inverse von TRUE).
In C gibt es einen Datentyp bool erst ab C99. Er ist in stdbool.h als Makro definiert; der eigentliche Datentyp heißt _Bool. Die boole'schen Werte true und false (wohlgemerkt klein geschrieben) sind in derselben Header-Datei definiert. Vor C99 kann man prinzipiell nach Gusto vorgehen. false oder FALSE werden üblicherweise als 0 definiert, true oder TRUE als 1 oder -1 - Hauptsache ungleich 0. Deine Variante ist ausgesprochen unüblich. Eher würde man FALSE = 0 und TRUE = ~FALSE definieren. Damit wäre TRUE die bitweise Negation von 0, was einer -1 gleichkommt.
kurt hat geschrieben: 12.03.2023, 10:26 Im Kapitel Strukturen bein ersten Code-Beispiel steht:

Code: Alles auswählen

struct struct Book {
Da ist sicherlich ein "struct" zu viel. Gemeint ist sicherlich:

Code: Alles auswählen

struct Book {
denn sonst würde "struct" als Struktur "Book" definiert werden, was sicherlich nicht beabsichtigt ist (wie im 2ten Code-Beispiel gezeigt).
Danke für den Hinweis; ich habe es korrigiert.
kurt hat geschrieben: 12.03.2023, 10:26 Ich habe im 3ten Code-Beispiel zur Buch-Struktur einmal den ersten Text-String verdreifacht. Der HT-C Compiler meckert jedenfalls nicht, wenn die zuzuweisende (statische) Zeichenkette länger ist wie der dafür resevierte Speicher. Ist das generell so, das vom Compiler hier nichts kommt, obwohl er das erkennen könnte ? ist im Grunde kein Beinbruch, ist sicher besser Eingabetexte auf max. Länge zu kontrollieren, aber bei statische Sachen sollte der Compiler aufmucken (also gewissenmaßen Copy-und-Paste Fehler abfangen, denn statische Zeichenketten zählt man selten nach).
In C wird bei der Array-Indizierung keine Prüfung durchgeführt - weder beim Kompilieren noch zur Laufzeit. Die Verantwortung liegt hier beim Programmierer, was zu einer häufigen Fehlerquelle führt. Pascal prüft standardmäßig die Indexgrenzen, was aber zu Lasten der Performance geht (deshalb kann man die Prüfung auch per Compiler-Schalter deaktivieren). Es sei noch darauf hingewiesen, dass es in string.h neben strcpy auch die Funktion strncpy gibt, die über einen zusätzlichen Parameter eine Begrenzung der Anzahl kopierter Zeichen sicherstellt. Sie ist insbesondere dann wichtig, wenn man nicht sicherstellen kann, dass der zu kopierende String die Länge des Ziel-Arrays nicht überschreitet. Beim Kopieren von String-Konstanten benutzt man aus Effizienzgründen lieber strcpy.
von bbock
09.03.2023, 15:18
Forum: Programmierung
Thema: Fragen und Antworten zum C-Kurs
Antworten: 108
Zugriffe: 501420

Re: Fragen und Antworten zum C-Kurs

Hallo Paul,
ich wünsche dir gute Besserung! Schade, das wir uns dieses Wochenende nicht sehen werden.
von bbock
08.03.2023, 13:20
Forum: Programmierung
Thema: Programmieren in C mit dem z88dk
Antworten: 20
Zugriffe: 103263

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: 103263

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: 155554

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: 103263

Ü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: 501420

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.