Computer Basics in C
Computer Basics in C
Anforderungen an Inspirer
- Ihr habt alle Kursinhalte wirklich gut verstanden und könnt sie auch flüssig reproduzieren. Wahrscheinlich auch einiges darüber hinaus - Fragen zur Architektur/Funktionsweise von Computern sollten kommen, und ihr solltet auch von weiterführenden Materialien wissen.
- Ihr wisst, wie eine Unix-Shell funktioniert, und kannst eine verständliche und hilfreiche Einführung dazu halten (+ weißt, wie man manpages öffnet/nutzt). -> Ihr habt selbst verstanden, wie die Sprache C in (fast alle bisherigen) Betriebssysteme eingebettet ist.
- Ihr könnt mit smarten Teilnehmenden jeden Alters so kommunizieren, dass sie sich ernst genommen fühlen.
- Ihr könnt auch erfahreneren Teilnehmenden Aufgaben geben, die sie nicht unterfordern - das könnte bedeuten, dass ihr sie schon früher mit den Inhalten im Endprojekt eigenständig herumspielen lässt.
- Ihr seid in der Lage, den Kurs so abzuändern, dass er allen Teilnehmenden maximal viel Spaß macht, und dabei sowohl sein eigenes Interesse zum Ausdruck bringt, als auch das Interesse der Teilnehmenden fördert.
Im Verlauf des Kurses werden wir uns die Kommandozeile, einfache Ein- und Ausgaben, Chars und Strings anschauen sowie unser eigenes auf der Kommandozeile ausführbares Programm schreiben.
Dabei können die Teilnehmenden (und vielleicht auch ihr) hoffentlich immer besser verstehen, wie Computer auf einer niedrigeren Ebene wirklich funktionieren. Ich hoffe, ihr seid so gespannt wie ich. Viel Spaß!
Zum Vorgehen: Passt euch, hier noch stärker als sonst, an die Teilnehmenden und deren Geschwindigkeit an. Gerade hier ist es besonders wichtig, Zusammenhänge zu verstehen und nicht nur "alles mal gesehen" zu haben.
Und, wie immer: Solltet ihr die Einführung nicht gelesen haben – und wollt diesen Kurs halten – dann holt das doch bitte nach :)
Kennenlernen
Vorstellungsrunde mit Programmiererfahrung/Motivation
Ihr stellt euch vor und erzählt, wie ihr zur Hacker School und zum Coden gekommen seid.
Nun die Teilnehmer:
- Wie heißt Du? (Achtet darauf, dass die Kinder sich nur mit Vornamen vorstellen – Datenschutz.)
- Warum bist Du hier?
- Hast Du schon mal programmiert?
- Was willst Du lernen?
Vorstellung interaktiver gestalten? (!Klick mich an!)
In unserem Inspirer Handbuch findet ihr
weitere Spielideen.
Wenn Du die Vorstellung etwas interaktiver gestalten möchtest, empfehlen wir das Spiel “Alle, die”, was im folgenden erklärt wird:
Spielidee und Ziel
- Wir kommen locker und leicht mit den Teilnehmer*innen ins Gespräch und erfahren, was sie gerne mögen (positiver Beziehungsaufbau).
- Wir können eine erste Diagnose stellen, was die Schülerinnen und Schüler können, was sie gerne mögen.
Ablauf
- Stellt Euch vor die Klasse.
- Ein*e Inspirer sagt: “Alle, die”-Frage z.B: “Alle, die Sport in der Freizeit machen?”.
- Alle, die sich angesprochen fühlen, stehen von ihrem Platz auf.
- Die*der Inspirer stellt Rückfragen und kommt ins Gespräch mit den Teilnehmer*innen, z.B. “Cool, du in der ersten Reihe: Wie heißt du? [Antwort] Welchen Sport machst du denn gerne? [Gespräch entsteht] Super und du in der letzten Reihe…”.
- Die*der Inspirer sagt: "Alle wieder setzen".
- Die*der Inspirer stellt die nächste Frage: z.B. “Alle, die gerne Spiele auf dem Handy oder Computer spielen!”
- Siehe 3.-5.
- Spielt das Ganze etwa 5 min (nach Gefühl)
Sammlung möglicher Fragen (!Klick mich an!)
Diese Sammlung kann gern erweitert oder angepasst werden.
IT/Programmieren
- Wer hat schon mal programmiert?
- Wer kann sich vorstellen etwas mit Programmieren und IT zu machen?
Hobbies
- Wer von Euch macht Sport in der Freizeit?
- Wer spielt ein Instrument?
- Wer spielt Computerspiele?
- Wer spielt Handyspiele?
Lustiges
- Wer hat eine Schuhgröße größer als 35?
Letzte Frage
- Wer ist schon in repl.it angemeldet?
printf()
Wir beginnen – wer hätte es gedacht – mit dem "Hello, World"-Programm.
Dabei "drucken" wir mithilfe von printf
Hello, World! auf der Kommandozeile aus. Das geht so:
1 2 3 4 5 6 |
|
printf()
kann noch deutlich mehr, und auf Einiges davon werden wir im Verlauf des Kurses noch stoßen.
Variablen und Typen
So. Nachdem wir jetzt grundlegend Dinge ausgeben können, ab zum nächsten Teil: Variablen und deren Benutzung.
Zunächst einmal muss man eine Sache wissen: Variablen besitzen in C einen Typ. Sie können zum Beispiel Zahlen (Typen: short
, int
, unsigned int
, float
) oder Buchstaben (Typ: char
) sein.
Daraus ergibt sich folgender Code:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
(Spätestens) hier ergibt es Sinn, Folgendes zu erklären:
printf()
und wie es funktioniert (%i
steht für integer,%c
für char)#include
s- Und wahrscheinlich auch Deklarierung und Initialisierung
Funktionen
Schreiben wir unsere Programme allerdings weiterhin auf diese Art und Weise, werden sie schnell unübersichtlich. Eine Möglichkeit, das zu vermeiden, sind Funktionen:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
Wichtige Punkte:
- Der Rückgabewert bzw. der Typ der Funktion
- Die Parameter und deren Typen
return
, und wie es mit dem Funktionstyp zusammenhängt.- Die Reihenfolge, in der man die Funktionen schreiben muss -> eine Funktion, die ich aufrufen will, muss vorher deklariert worden sein.
Hier ist es super wichtig, dass die Teilnehmenden wirklich verstehen, was passiert. Das rächt sich sonst später. Daher nochmal eine Erinnerung:
Lasst die Teilnehmenden Dinge direkt ausprobieren. Wie wäre es mit einer anderen Funktion – vielleicht int square(int num)
, int triple(int num)
oder add_three(int a, int b, int c)
?
Und weiter geht´s.
if/else
Eine weiter Grundlage, die bisher gefehlt hat, sind Verzweigungen. Wir treffen auch im echten Leben Entscheidungen in Abhängigkeit von bestimmten Bedingungen – wir nehmen eine Regenjacke mit, wenn es regnen soll, oder essen nur dann Eis, wenn es wärmer als -5°C ist. Auch beim Programmieren ist das äußerst nützlich.
Wie stellen wir das an?
In C geht das so:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
Oder doch lieber in einer Funktion?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
|
chars und ASCII
Für alle Teilnehmenden, die vorher schon programmieren konnten, war das bisher nur eine Wiederholung der Basics.
Hier kommt etwas, das C gegenüber anderen Programmiersprachen besonders macht. Schaut euch diesen Code an.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
So richtig gut scheint nicht alles zu sein. Vielleicht können ja die Teilnehmenden – gemeinsam mit euch – dem Problem auf den Grund gehen?
Eure Lösung führt euch dann hoffentlich über ein bisschen Nachdenken, eine Internet-Suche und die ASCII-Tabelle zurück zu eurem C-Code, in dem die Teilnehmenden ihre Erkenntnisse verarbeiten können.
Um dann, gemeinsam mit euch, zur Erkenntnis zu kommen:
Das hier wäre ein guter Moment, eine Pause zu machen :)
Arrays
Davor kommt am besten erst einmal ein: "Hallo zurück!" Fragt doch die Teilnehmenden, wie es ihnen geht, was die beste Sache war, die ihnen diese Woche passiert ist, ...
Es lohnt sich, ihnen so erst einmal Zeit zum Ankommen zu lassen (und wird wahrscheinlich auch euch gut tun ;))
Danach könnt nämlich auch gut den bisherigen Inhalt rekapitulieren.
Wir können:
- Auf die Kommandozeile schreiben (
write()
/printf()
) - Dinge speichern (Variablen)
- Aufgaben unterteilen (Funktionen)
- Code abhängig von Conditions ausführen (if/else)
Was wäre, wenn man sehr viel speichern möchte? -> Auflösung: Man könnte nicht nur für Einzelelemente Namen vergeben, sondern für ganze Bereiche auf einmal. Das kann man mit einem Array machen.
Und zwar so:
1 2 3 4 5 6 7 8 9 10 11 |
|
Loops
Aber was, wenn wir jetzt alle Werte aus dem Array auf einmal ausgeben wollen?
Oder genereller: Wie wiederholen wir Anweisungen beliebig oft?
Dafür gibt es loops (oder Schleifen). Den while
-loop benutzt man so:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
Wichtige Punkte:
- Die Variablen sollten vor
while
initialisiert sein. - Was macht
count++
eigentlich?
Und schon können wir über beliebige Arrays iterieren.
Doch entgegen dem Beispiel im Code können wir Schleifen natürlich nicht nur mit Arrays verwenden. Mit Schleifen kann man jede Anweisung eine beliebig oft ausführen. Falls ihr das Gefühl habt, das ist den Teilnehmenden noch nicht so ganz klar, macht hier gerne noch ein Beispiel dazu (oder fangt gleich mit einem einfacheren Beispiel an:))
Strings
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
|
Was fällt auf?
Hinter dem string
steht immer eine 0
(die wir nicht sehen können), und das unabhängig davon, was in string
steht. Dieser ASCII-Wert (0
) steht für den NULL-Terminator ('\0'
), mit dem jeder (C-)String beendet wird.
Das ermöglicht uns zum Beispiel, Strings sehr einfach ausgeben zu lassen:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
|
Also: Ein String ist ein Array von char
s, der mit '\0'
terminiert ist.
Und damit bleibt es dabei: In C gibt es nur Zahlen, und Arrays von Zahlen.
Pointer
Eine weitere Sache, die C besonders macht:
Alle Speicherplätze in unserem Computer haben Speicheradressen, auf die man mithilfe von Pointern (oder Zeigern) zeigen kann. Eine solche Speicheradresse sieht auf einem 32-bit-System so aus:
0x0e469a7d
(Eine Ziffer im Hexadezimalsystem ≙ 4 bit)
Mit diesen Speicheradressen, vor allem aber ihren Pointern, können wir in C direkt arbeiten. Zum Beispiel so:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
Einige Anmerkungen:
- Der
*
in Zeile 3 zeigt den Typ der Variable num_pointer an. (Der Typ ist Pointer aufint
) - Der
*
in Zeile 5 macht etwas anderes: Er dereferenziert den Pointernum_pointer
- Das
&
in Zeile 13 gibtwrite_seventeen()
eine Referenz aufnumber
(also einen Pointer aufnumber
) mit.
Anmerkung zur Anmerkung: Gerade gegen Anfang können Pointer sehr verwirren – malt euch/den Teilnehmenden doch da eine Grafik :) (Auf Repl kann man sich mit <dateiname>.draw
ein Whiteboard erstellen.)
All das wirkt jetzt wahrscheinlich ziemlich umständlich dafür, dass wir nur den Wert einer Variable ändern wollen. Es ist allerdings notwendig, da das der einzige Weg ist, wie man in C Variablen innerhalb einer anderen Funktion mutieren kann. Funktionen bekommen ansonsten nämlich nicht die Aufrufargumente selbst, sondern lokale Kopien von ihnen mitgegeben. Das ist als Schutz gedacht, durchaus sinnvoll ist. Da wir diesen hier aber absichtlich umgehen wollen, müssen wir das auf diese Art und Weise explizit(er) machen.
Da Pointer aber auch 'nur' Zahlen sind, können wir ganz normal mit ihnen rechnen. Folgt man diesen veränderten Pointern allerdings, kann alles Mögliche passieren.
*(num_pointer + 10)
sagen ;)Passt also auf, wenn ihr mit Pointern arbeitet.
Kurz zusammengefasst: Ein Pointer ist ein Zeiger auf einen Speicherplatz, dem man mit *ptr
folgen kann, um den Inhalt des Speicherplatzes zu erhalten.
int main(int argc, char *argv[])
So. So langsam kommen wir unserem Ziel näher. Wir sind nämlich schon sehr bald in der Lage, unser erstes eigenes Kommandozeilenprogramm zu schreiben, das Argumente annehmen und verarbeiten kann.
Doch testet zunächst einmal, was unsere bisherigen Programme tun, wenn man ihnen Argumente mitgibt. Dafür hören wir damit auf, auf den Run-Button zu drücken.
Wir kompilieren unser Programm stattdessen von Hand, um es danach dann auch von Hand ausführen zu können.
Das macht ihr, indem ihr rechts von Console auf Shell wechselt. Dort gebt ihr dann gcc main.c
ein. Eine Datei namens a.out
sollte erscheinen. Herzlichen Glückwunsch! Das ist euer erstes selbst kompiliertes Programm.
1 2 3 | $ gcc main.c $ ls a.out main main.c |
Ein Programm führt ihr aus, indem ihr ./<programm-name>
schreibt:
1 | $ ./a.out hi ich bin
|
Was passiert?
Richtig. Gar nichts. Das liegt daran, dass wir main()
ja auch noch gar nicht sagen, dass wir mit seinen Argumenten etwas anfangen möchten. Das können wir erst, wenn wir die main
-Funktion so abändern:
1 | int main(int argc, char *argv[]); |
Anstatt dass wir void, also nichts, mitgeben, ist hier argc
nun die Anzahl von Argumenten, die wir mitgeben, und argv ein Array von Pointern, die auf Strings zeigen.
Lasst uns die Argumente benutzen, um sie auszugeben.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
Projekte
Und damit sind wir auch schon (fast) am Ende unserer ziemlich langen Lernphase angekommen. Nun geht die Initiative endgültig zu den Teilnehmenden. Gibt es etwas, das sie gerne selbst coden/ausprobieren würden?
Macht noch ein letztes Mal deutlich, dass ihr hier seid, um ihnen zu helfen.
Falls es wenig bis keine Ideen gibt, lohnt es sich vielleicht auch, sie einfach über die neu gelernten Dinge sprechen zu lassen.
Ansonsten hier ein paar Ideen:
Ein Taschenrechner, der folgende/oder eine ähnliche Funktionalität hat:
1 2 3 4
$ ./a.out + 2 3 3 5 2 8 6 29 $ ./a.out * 2 3 1 4 24
Ein Programm, das ausgibt, welcher von zwei eingegebenen Strings länger ist
1 2 3 4 5
$ ./a.out hallo hi Der Erste $ ./a.out ich bins Der Zweite $ ./a.out ich du wir
Was, findet ihr, sollte das Programm im letzten Fall zurückgeben/machen?
Optional(!): Assembly Code
Bisher haben wir uns sehr erfolgreich auf der Höhe der Shell vorangearbeitet.
Falls es immer noch jemanden gibt, der noch Lust auf mehr hat:
Die erste Hälfte der Antwort liefert uns der Befehl
1 | $ gcc -S main.c
|
Dieser produziert eine Datei mit der Endung .s
, in diesem Fall die Datei main.s
. Dort findet ihr die Befehle, die der Computer "in Wirklichkeit" ausführt. Von hier aus bis zu echtem Maschinencode ist es dann nur noch ein sehr kleiner Schritt, das ist dann allerdings nicht mehr/nur noch sehr schwer von Menschen lesbar. Diese Aufgabe übernimmt in unserem Fall der C-Compiler, ansonsten könnte sie aber auch ein sogenannter Assembler übernehmen.
Auch hiermit kann man sehr gut herumspielen, und mit einer kleinen Internetrecherche findet man auch heraus, was die meisten Assemblerbefehle machen.
Aber das ist vielleicht einmal Teil eines anderen Kurses – dieser endet nämlich hier.
Macht gerne noch eine große Abschlussrunde mit den Teilnehmenden, in der sie sich gegenseitig ihre Projekte vorstellen können – oder auch einfach nur euch ausfragen und sich Feedback abholen.
Großen Respekt euch, das bis hierher gelesen zu haben durchgehalten zu haben. Ich hoffe, es hat Spaß gemacht, und ihr habt auch ein bisschen mehr über C gelernt. Und vergesst nicht, auch wir freuen uns über Feedback :)