Ein Programm ist zum Laufen zu bringen, welches den Text Hallo! auf den Bildschirm schreibt.
Für diese Aufgabe ist die Funktion printf() nötig; diese ist in stdio.h definiert.
Der Text Hallo! ist in einer Zeile auf den Bildschirm zu schreiben, danach
sollen 2 Leerzeilen kommen und in der 4. Zeile der Text:
Ich bin Dein dummer Rechner!
.
Die ganze Ausgabe soll also so aussehen:
Hallo! Ich bin Dein dummer Rechner!
Das Zeichen
soll dabei ein Leerzeichen darstellen;
die letzte Zeile sollte also mit 4 Leerzeichen beginnen.
Die Quadratzahlen (1, 4, 9, 16, usw.) bis 100 sind in jeweils einer Zeile auf den Bildschirm zu schreiben.
Die Zahlenfolge -1, 1, -4, 4, -9, 9, -16, 16 ist analog zur vorherigen Aufgabe auszugeben.
In dem folgenden Fragment ist die Funktion tage()
zu ergänzen:
#include <stdio.h> int tage( int monat ) { /* ??? */ } main() { printf( "Januar hat %d Tage\n", tage(1) ); printf( "Februar hat %d Tage\n", tage(2) ); printf( "Maerz hat %d Tage\n", tage(3) ); printf( "April hat %d Tage\n", tage(4) ); printf( "Mai hat %d Tage\n", tage(5) ); printf( "Juni hat %d Tage\n", tage(6) ); printf( "Juli hat %d Tage\n", tage(7) ); printf( "August hat %d Tage\n", tage(8) ); printf( "September hat %d Tage\n", tage(9) ); printf( "Oktober hat %d Tage\n", tage(10) ); printf( "November hat %d Tage\n", tage(11) ); printf( "Dezember hat %d Tage\n", tage(12) ); }
Schaltjahre brauchen nicht berücksichtigt zu werden.
tage()
soll also beim Aufruf mit dem Argument 1 den Wert 31
liefern, beim Aufruf mit 2 den Wert 28 usw..
Bei dieser und den folgenden Aufgaben werden Zeiger benötigt. Weiterhin muß man wissen, wie in C ein String aussieht (abschließende Null!).
Die Funktion
char *strcpy( char *wohin, const char *woher );ist zu programmieren und auszutesten.
strcpy() kopiert ab woher Zeichen nach wohin, bis das Stringende von woher erreicht ist, also bis ein Nullbyte kopiert wurde. Zurückgeliefert wird ein Zeiger auf die erzeugte Zeichenkette, also der Wert von wohin.
Die Funktion
char *strcat( char *wohin, const char *woher );ist zu programmieren und auszutesten.
strcat() kopiert ab woher Zeichen hinter das letzte Zeichen des Strings ab wohin, bis das Stringende von woher erreicht ist, also bis ein Nullbyte kopiert wurde. Zurückgeliefert wird ein Zeiger auf die erzeugte Zeichenkette, also der Wert von wohin.
Mit einem Datentyp
typedef char pstring[81];lassen sich Pascal-ähnliche Strings nachbilden.
Das nullte Element speichert dabei die aktuell verwendete Länge; in den Positionen 1 bis maximal 80 wird der eigentliche String gehalten.
Solche Strings dürfen im Gegensatz zu C-Strings auch Nullbytes speichern, da dieses Zeichen nicht als Stringende benötigt wird.
Dementsprechend können die Funktionen aus der C-Bibliothek nicht zur Manipulation der pstrings verwendet werden.
Aufgabe ist es, folgende Funktionen zu schreiben:
kopiert einen C-String cs bis zur abschließenden 0 in einen Pascalstring ps.
Nach dem Kopieren der eigentlichen Zeichen an die Stelle ps[1] und folgende muß natürlich noch die Anzahl der kopierten Zeichen in ps[0] eingetragen werden.
Zurückgegeben wird ein Zeiger auf den erzeugten Pascalstring, also ps.
kopiert den Inhalt eines Pascalstrings in eine normalen C-String und hängt dabei die Null am Stringende an.
Rückgabe ist ein Zeiger auf den erzeugten C-String, also cs.
kopiert den String woher nach wohin.
Rückgabe ist ein Zeiger auf den neuen String, also wohin
.
hängt an den String woran den String woher an.
Rückgabe ist ein Zeiger auf den neuen String, also woran.
ist identisch mit den anderen printf()-Versionen, schreibt aber das Ergebnis in einen Pascalstring ps (Achtung: Die Lösung ist nur einige Zeilen lang!).
Die Funktionen brauchen keine Fehlerüberprüfung.
Das folgende Programm ist um den fehlenden Rest der Funktion schreibwas() zu ergänzen:
/* Hier ist putchar() vereinbart: */ #include <stdio.h> /* Der String ab der Stelle s wird zeichenweise auf dem * Bildschirm ausgegeben, anschließend ein Zeilenvorschub. */ void schreibwas( char *s ) { /* ??? */ } /* Testprogramm. */ main() { schreibwas( "hallo!" ); schreibwas( "nochmal hallo!" ); }
schreibwas() soll den übergebenen String zeichenweise auf den Bildschirm ausgeben und anschließend einen Zeilenvorschub erzeugen, also genauso arbeiten wie die Funktion puts() aus der Standardbibliothek. puts() ist für die Lösung dieser Aufgabe aber nicht zu benutzen! Vielmehr soll der String zeichenweise ausgegeben werden, bis die abschließende Null erreicht ist.
Nötige Kenntnisse: Strings, Felder, Strukturen, printf() und scanf() aus der Standardbibliothek.
Zu schreiben ist ein kleines Programm, welches eine Struktur als Datentyp vereinbart zum Speichern einer Person. Eine Person soll aus Vorname, Nachname und Alter bestehen.
Von diesem Datentyp ist ein Feld für 5 Personen zu definieren.
Weiterhin soll eine Funktion enthalten sein, welche die Daten einer Person von der Tastatur einliest, und eine Funktion, welche die Daten einer Person ausgibt.
Das Hauptprogramm liest in einer Schleife mit der erstgenannten Funktion alle 5 Personen ein. Danach erst werden mit der zweiten Funktion alle 5 Personen wieder ausgegeben.
Für diese Aufgabe muß man etwas mit Zeigern umgehen können, außerdem braucht man casts für Typumwandlungen, Zeiger auf Funktionen und Felder.
Das Beispiel zum Aufruf der zu schreibenden Funktion verwendet Strukturen; für die Aufgabe selbst ist deren Kenntnis aber unwichtig.
Eine Funktion mit der Deklaration
void *lsearch( const void *schluessel, const void *feldanfang, size_t elemente, size_t elementgroesse, int(* vergleichsfkt)( const void *e1, const void *e2 ) );
ist zu schreiben. Die Funktion soll in einem eventuell unsortierten Feld ab feldanfang mit linearer Suche ein Element finden, für das die Funktion (*vergleichsfkt)() beim Vergleich mit dem Element (*schluessel) den Wert 0 liefert, also Gleichheit.
Wird ein solches Element gefunden, dann wird ein Zeiger darauf zurückgegeben. Wird kein Element gefunden, dann soll der NULL-Zeiger zurückgegeben werden.
Der Aufruf ist identisch mit dem von bsearch()
aus der
Standardbibliothek.
Das Feld ab feldanfang muß (elemente) Werte beinhalten; jedes Element hat die Größe (elementgroesse).
vergleichsfkt zeigt auf eine Funktion, die man mit 2 Zeigern als Parameter aufrufen kann und die ein Resultat vom Typ int liefert. Wenn die Parameter auf Daten von dem Typ zeigen, den auch jedes Element in dem Feld ab feldanfang hat, dann muß diese Funktion den Wert 0 liefern, wenn die beiden Daten als gleich zu betrachten sind.
Für den Parameter vergleichsfkt kann man also gegebenfalls
die gleiche Funktion verwenden wie für bsearch()
und
qsort()
.
Der Rückgabewert von lsearch()
ist ein Zeiger auf das gefundene
Element, oder NULL,
wenn in dem Feld kein Element steht, für das
(*vergleichsfkt) beim
Vergleich mit dem Schlüssel (*schluessel)
den Wert 0 liefert.
lsearch()
soll ebenso wie qsort()
oder bsearch()
für beliebige Datentypen
funktionieren, da die Behandlung der Daten (Feld und Schlüssel)
nur in der übergebenen Vergleichsfunktion erfolgt, die wiederum
vom Aufrufer zur Verfügung zu stellen ist.
lsearch()
soll von der Funktion und
vom Aufruf her identisch sein mit
bsearch()
, außer daß das Feld für
bsearch()
sortiert sein muß.
Für lsearch()
ist dies nicht nötig.
typedef struct { char name[30]; /* Autoname */ int iq; /* IQ des Fahrers */ } Auto_t;
In dem Beispielprogramm wird von diesem Typ ein Feld vereinbart und mit einigen Werten initialisiert.
Der Benutzer wird beim Programmlauf aufgefordert, einen Autonamen
einzugeben. Dann sucht das Programm mit der noch unbekannten
Funktion lsearch()
nach dem Feldelement, in dem der eingegebene Autonamen enthalten ist
und gibt den zugehörigen IQ aus.32
/* aulsear.c 30. 7.95 kw * Beispielprogramm fuer lsearch(). */ #include <string.h> /* lsearch() deklarieren: */ void *lsearch( const void *schluessel, const void *feldanfang, size_t elemente, size_t elementgroesse, int(* vergleichsfkt)( const void *e1, const void *e2 ) ); /* Den Datentyp Auto_t vereinbaren: */ typedef struct { char name[30]; /* Autoname */ int iq; /* IQ des Fahrers */ } Auto_t; /* Davon ein Feld vereinbaren und initialisieren: */ #define N 4 Auto_t Auto[N] = { { "525", 40 }, { "190", 20 }, { "Manta", 0 }, { "Polo", 80 } }; /* Die fuer lsearch() noetige Vergleichsfunktion erhaelt * zwei Zeiger auf Daten, in unserem Fall vom Typ Auto_t. * Sind die darin enthaltenen Autonamen gleich, dann * soll der Wert 0 geliefert werden. * Die Unterscheidung zwischen "groesser" und "kleiner" * ist fuer lsearch() nicht noetig, wird aber so gleich * mitgeliefert: */ int verglname( void *Auto1, void *Auto2 ) { return strcmp( ((Auto_t*)Auto1)->name, ((Auto_t*)Auto2)->name ); } /* Das Hauptprogramm liest von der Standardeingabe einen * Autonamen und versucht, den IQ dazu zu finden: */ int main() { Auto_t suchauto, /* danach suchen */ *gefunden; /* Zeiger auf gefundenes */ /* Auto */ /* Den Autonamen lesen: */ puts( "Bitte einen Autonamen:" ); gets( suchauto.name ); /* suchauto dient fuer lsearch() als Schluessel. * Da in der Vergleichsfunktion verglname() nur * der Name verwendet wird, ist der iq in suchauto * egal. */ /* Jetzt mit lsearch() einen Zeiger auf das Element * holen, das den gleichen Namen enthaelt wie * suchauto: */ gefunden = lsearch( &suchauto, Auto, (size_t)N, sizeof(Auto_t), verglname ); /* Wenn gefunden ungleich NULL ist, dann wurde ein * Element gefunden: */ if( gefunden ) printf( "Der IQ eines %s-Fahrers ist etwa %d\n", gefunden->name, gefunden->iq ); else printf( "%s-Fahrer kenne ich nicht.\n", suchauto.name ); return 0; }
Gesucht ist also die Funktion lsearch()
.
Voraussetzungen: Öffnen von Dateien und Lesen daraus.
Aus der Textdatei werte.dat, die so aussehen kann:
4711 4712 42 0 0 4713
sollen Zahlenwerte vom Typ int gelesen werden, bis das Dateiende erreicht wird.
Das zu schreibende Programm soll die Zahlen einlesen, die Anzahl der gelesenen Werte ausgeben sowie den Durchschnitt der Zahlen.
Welchen Wert hat in C der Ausdruck -1<0<1 ?
Hierfür muß man mit Zeigern gut umgehen können.
Die Aufgabe lautet: Was macht das folgende Programm33? Kann es überhaupt richtig funktionieren?
/* aujong1.c 30. 7.95 kw * Was macht dieses Programm? */ char *c[] = { "he dast ga", "lllt dumm", "C i", "dar nich" }; char **cp[] = { c+3, c+2, c+1, c }; char ***cpp = cp; main( int Anzarg, char **Arg ) { printf( "%s", **++cpp ); printf( "%s", *--*++cpp+5 ); printf( "%s", cpp[-2][0]+2 ); printf( "%s\n", *(cpp[1]+1)+3 ); getchar(); }
Das Programm soll nicht abgetippt und ausprobiert werden, bis die Lösung gefunden ist! Vielmehr soll die Lösung nur durch Überlegen gefunden werden. Das Aufzeichnen der Variablen auf einem Blatt Papier erleichtert die Lösung enorm.
Noch mehr Aufgaben dieser Art findet man in [C-Puzzle].
Für diese Aufgabe muß man etwas mit Zeigern umgehen können, außerdem braucht man casts für Typumwandlungen, Zeiger auf Funktionen und Felder. Schließlich muß man auch die Rekursion verstanden haben.
Aufgabe ist, die Funktion qsort() aus der Standardbibliothek selbst zu schreiben.
Zum Testen kann das Beispielprogramm zu qsort() zweckentfemdet werden.
Der quick sort-Algorithmus sortiert ein Feld folgendermaßen:
Die Rekursion bricht ab, wenn ein Teilfeld weniger als zwei Elemente hat.
Zur Auswahl des Elementes: Am günstigsten wäre es, das Element so zu wählen, daß die entstehenden Teilfelder etwa gleich groß sind. Da aber dieses Element nicht bekannt ist, wählt man einfach das Element in der Feldmitte.
Zu schreiben ist ein Programm pp
, welches einige Funktionen des
Präprozessors von C ausführt.
Folgende Funktionen sollten mindestens enthalten sein:
Achtung! Die #if-Konstruktionen sollten geschachtelt werden können.
Als Bonusaufgabe darf das Programm auch gerne die #define-Anweisung mit Parametern beherrschen!
Die anderen Direktiven sowie die Spezialitäten # und ## brauchen nicht vorgesehen zu werden.
Das Programm soll mit zwei Parametern aufgerufen werden.
Der zu schreibende Praeprozessor liest also eine Datei und erzeugt in einer weiteren Datei eine Ausgabe. Nach den üblichen Regeln des C-Praeprozessors sollen dabei mit #include weitere Dateien in die Ausgabe eingefügt werden, mit #define definierte Namen durch ihren Wert ersetzt werden, und so fort.
Die mit #include eingefügten Dateien durchlaufen wiederum den Praeprozessor.
Da das fertige Programm unabhängig vom C-Compiler ist, kann man es genausogut zum Entwickeln von FORTRAN- oder Pascal-Programmen oder zur Arbeit mit TEX verwenden.
Auf Fehleingaben (zu wenige, zu viele Parameter, falsche Namen etc.) soll das Programm sinnvoll reagieren (Erklärung ausgeben, abbrechen...).
AnyWare@Wachtler.de