Einführung in die Programmiersprache C und in C-Zeiger

Inhalt und Lernziele des Kapitels Einführung in die Programmiersprache C und in C-Zeiger.

Einordnung des Artikels

Ein Vergleich zwischen C und C++, insbesondere Felder und Zeiger betreffend

C++ ist eine Weiterentwicklung von C.

Wenn man diesen Satz liest, fragt man sich sofort, warum man überhaupt C lernen soll oder sich speziell in Konzepte einarbeiten soll, die in C++ nicht mehr benötigt werden. Versucht man den Unterschied zwischen C und C++ knapp zu beschreiben, kann man vielleicht sagen:

Mit C++ erreicht man einen höheren Abstaktionsgrad, C erlaubt maschinennahe Programmierung.

Fragt man nach den Konzepte, die in C++ neu enthalten sind, so sollte man vor allem nennen:

Wer mit diesen Konzepten arbeiten möchte, kann sich bei der Wahl zwischen C und C++ nur für C++ entscheiden.

Wer dagegen nur im Sinne der prozeduralen Programmierung arbeiten möchte, kann sowohl auf C als auch auf C++ zurückgreifen. Wobei man aber die Vorteile beider Sprachen sofort nennen sollte:

Da C++ eine Weiterentwicklung von C ist, könnte man argumentieren, dass es für maschinennahe Programmierung unerheblich ist, für welche Sprache man sich entscheidet. Stehen aber nur begrenzte Ressourcen zur Verfügung (Speicherplatz, Prozessor), ist meist C die bessere Wahl.

Als weiteres Kriterium für die Unterscheidung zwischen C und C++ müssen Felder (array) und Zeiger (pointer) genannt werden. Auch hier kann man versuchen, den Unterschied knapp zu formulieren:

Dieser Kurs sollte eine Einführung in C++ bieten; dabei wurden die Konzepte so präsentiert, dass man schnell zum höheren Abstraktionsgrad gelangt. Wenn man jetzt wieder einen Schritt zurückgeht und – die gerade als nahezu überflüssig bezeichneten – Zeiger erläutert, soll auch erklärt werden, welchen Vorteil die Beschäftigung mit Feldern und Zeigern bieten kann:

Inhalte der Einführung in die Programmiersprache C und C-Zeiger

Der Teil Einführung in die Programmiersprache C und in C-Zeiger des Kurses Einführung in die Informatik soll eine Lücke schließen, die die Teile über C++ hinterlassen haben. Letztere wurden – wie oben ausführlich geschildert – so konzipiert, dass Felder nicht mehr eingesetzt werden, sondern durch die Klassen array und vector ersetzt werden. Diese Klassen beinhalten eine Vielzahl an Methoden, die immer wiederkehrende Funktionalitäten beim Umgang mit Feldern bereitstellen.

Wer dagegen mit C arbeiten möchte, kann auf diese Klassen und Methoden nicht zurückgreifen und ist gezwungen sich in Felder und Zeiger einzuarbeiten, die in diesem Teil vorgestellt werden. Dabei werden aber nicht alle mit Feldern und Zeigern zusammenhängenden Konzepte vorgestellt; die Auswahl bezieht sich auf diejenigen, die zum Einstieg unerlässlich sind.

Bevor Felder und Zeiger besprochen werden, sollen die wichtigsten Sprach-Konzepte von C kurz dargestellt werden. Dabei handelt es sich um:

Diese Konzepte werden nicht so ausführlich dargestellt wie bei C++, sie sollen nur exemplarisch an einfachen Programmen aufgezeigt und kurz erläutert werden. Dabei wird aber darauf geachtet, dass keine Sprachelemente von C++ verwendet werden, die in C nicht verfügbar sind.

Kurze Inhaltsangabe

Fehlende Teile

Es soll ausdrücklich betont werden, dass hier nur eine Einführung in die Programmiersprache C und in Felder und Zeiger geboten wird und daher nicht alle Aspekte dieser und verwandter Konzepte behandelt werden. Um einige Konzepte zu nennen, die hier nicht behandelt werden:

Lernziele

Gerade für Programmier-Anfänger, die wenig mit der Arbeitsweise und Architektur eines Computers vertraut sind, steht mit Feldern und C-Zeiger eine längere Durststrecke bevor. (Im ersten Teil dieses Informatik-Kurses über Rechnerarchitektur können die Kenntnisse über Computer unabhängig von einer Programmiersprache und von Programmiertechniken nachgearbeitet werden.)

Eine "Durststrecke" steht bevor, da einige spezielle Eigenschaften und Spitzfindigkeiten von Feldern und Zeigern diskutiert werden müssen, ehe sie in relevanten Anwendungen eingesetzt werden können. Es ist daher dem Leser dringend empfohlen, sich zunächst – zumindest oberflächlich – klarzumachen, wozu Felder und Zeiger eigentlich eingeführt wurden. Hier ein kurzer Überblick:

  1. Mit Zeigern kann der Mechanismus call by reference realisiert werden. Damit ist gemeint, dass bei einem Funktionsaufruf keine Kopien angelegt werden, um die aufrufenden Argumente an die Funktion weiterzugeben. Stattdessen werden lediglich Adressen weitergereicht, mit denen die Argumente angesprochen werden können. Damit kann man umfangreiche Kopiervorgänge vermeiden.
  2. Der Mechanismus call by reference kann auch genutzt werden, um ein Objekt außerhalb einer Funktion zu verändern – man muss dazu nur dessen Adresse kennen. Dadurch ist es nicht nötig, in einer Funktion einen Rückgabewert zu berechnen (den man dann wieder an die aufrufende Instanz kopieren müsste).
  3. Letzteres kann man auch verwenden, um Funktionen mit mehreren Rückgabewerten zu simulieren, die es in C nicht geben kann (genauer: man müsste die Rückgabewerte erst zu einem Objekt verpacken).

1. Einführung in die Programmiersprache C: HelloWorld und erste Programme:

Wie immer wird zuerst eine HelloWorld-Anwendung gezeigt, um zu testen, ob die Arbeitsumgebung fehlerfrei funktioniert. Anschließend werden einige einfache Programme diskutiert, die im Sinne der prozeduralen Programmierung geschrieben sind.

Wer mit C++ vertraut ist, wird feststellen, dass sich auf diesem einfachen Niveau die Unterschiede zwischen C und C++ eigentlich nur auf zwei Elemente beziehen:

  1. Die inkludierten Bibliotheken werden anders angesprochen.
  2. Konsolen-Ausgaben erfolgen mit den Funktion puts() und printf().

2. Einführung in die Programmiersprache C: Schleifen mit for und while:

Die Schleifen werden nur exemplarisch dargestellt, hier gibt es keinen Unterschied zwischen C und C++.

3. Einführung in die Programmiersprache C: Funktionen:

Mit Funktionen lassen sich Quelltexte besser strukturieren und man vermeidet Wiederholungen des Quelltextes, indem man öfters auftretende Anweisungen zu einer Funktion zusammenfasst, die dann an beliebiger Stelle aufgerufen werden kann.

Um nicht nur bestehende Funktionen aufzurufen (etwa aus den Bibliotheken), sondern selbst Funktionen zu schreiben, muss man die Syntax beherrschen:

Wie werden Funktionen deklariert, aufgerufen und implementiert?

Die Syntax wird an mehreren Beispielen demonstriert, dabei wird auch aufgezeigt, welche "Design-Entscheidungen" man beim Entwurf von Funktionen treffen muss.

4. Definition und einfache Anwendungen von C-Zeigern:

Zeiger sind Variable, die eine Adresse speichern können, insbesondere die Adresse, unter der eine andere Variable abgespeichert ist. In diesem Kapitel sollen Sie:

5. Felder (arrays) in C:

Felder werden eingesetzt, wenn man mehrere gleichartige Variablen zu einem Objekt zusammenfassen möchte; "gleichartig" heißt hier, dass sie gleichen Datentyp besitzen. Um Felder geeignet einzusetzen, lernen Sie hier:

6. Anwendung von C-Zeigern: Der Zusammenhang zwischen Feldern und Zeigern:

Dieser Abschnitt soll die oben angekündigte "Durststrecke" beenden: jetzt werden die Kenntnisse über Felder und Zeiger zusammengeführt.

Intern werden nämlich Felder durch Zeiger organisiert und beispielsweise alle Zugriffe auf die Komponenten eines Feldes werden vom Compiler in Anweisungen mit Zeigern übersetzt. Und C ist eine der wenigen Sprachen, in denen man alle Anweisungen auf dieser niedrigeren Abstraktionsstufe selbst implementieren kann. Für einen souveränen Umgang mit Feldern ist es daher nötig, diese Organisation von Feldern, insbesondere die Zeigerarithmetik, zu kennen.

Der Abschnitt wird:

Es werden dabei viele Spitzfindigkeiten erneut auftreten, die in Definition und einfache Anwendungen von C-Zeigern gezeigt wurden, aber es wird besser verständlich werden, warum es nötig war, sie zu diskutieren.

7. Weitere Anwendungen von C-Zeigern: Der Zusammenhang von Feldern, Zeigern und Funktionen:

Wenn eine Funktion als Eingabewert einen primitiven Datentyp erwartet, sollte man sich vorstellen, dass beim Funktionsaufruf tatsächlich eine Kopie des Objektes an die Funktion übergeben wird. Dieser Mechanismus wird bei Feldern nicht verwendet, da man eventuell riesige Speicherbereiche kopieren müsste. Stattdessen wird die Übergabe eines Feldes an eine Funktion mit einem Zeiger realisiert.

Sie lernen in diesem Abschnitt, wie man den Funktionsaufruf realisiert und welche Besonderheiten sich daraus ergeben, dass nicht mehr ein Objekt kopiert wird.

8. Programmier-Aufgaben in C: erste Schritte mit Feldern und Zeigern

Die Aufgaben behandeln die typischen Schwierigkeiten, mit denen man konfrontiert wird, wenn man zum ersten Mal selbständig mit Feldern und Zeigern umgehen muss. Es wird dringend empfohlen, die Aufgaben selber zu lösen, bei Problemen erst nochmal in der Theorie nachzulesen und die Lösungen erst anzusehen, wenn man eine eigene Lösung entwickelt hat.

9. Programmier-Aufgaben in C: Anwendungen von Feldern, Zeigern und Funktionen:

Gerade für Programmier-Anfänger kann die Syntax von Variablen, Feldern, Zeigern eher verwirrend sein. Wichtig ist es dann nicht bei den einführenden Beispielen stehen zu bleiben, die lediglich die Spitzfindigkeiten erklären. Viel wichtiger ist, diese Konzepte in relevanten Anwendungen selber zu testen. Die hier vorgestellten Aufgaben bieten dazu ausreichend Gelegenheit. Sie sind alle konzipiert, dass Felder, Zeiger, Funktionen und der Mechanismus call by reference eingesetzt werden müssen.

Lösungen werden zu den Aufgaben nicht gegeben, da diese nur dazu verführen, deren Quelltexte anzusehen. Sie werden beim Umgang mit den genannten Konzepten aber schnell bemerken, wie wichtig es ist, eigene Versuche zu unternehmen und so auf die versteckten Fehlerquellen aufmerksam zu werden – Vorführen von Lösungen kann diesen Lernprozess niemals ersetzen.

C-Projekte in Code::Blocks

Wie man C++ Projekte mit Code::Blocks anlegt, wurde in Kennenlernen der Entwicklungsumgebung Code::Blocks ausführlich erklärt. Hier wird nur auf die kleinen Unterschiede eingegangen, wenn mit C anstelle von C++ gearbeitet wird. Dabei wird aber die Syntax von C nicht ausführlich erklärt.

Anlegen eines neuen Projektes

Um ein neues C-Projekt anzulegen, wählt man in Code::Blocks:

File -> New -> Project -> Console Application -> C Project

Anschließend muss man den Titel des Projektes und den Ordner festlegen. Ansonsten empfiehlt es sich, die vorgeschlagenen default-Einstellungen zu wählen.

Das erste Projekt HelloWorld

Das neu angelegte Projekt zeigt im Projekt-Explorer einen Ordner Sources für die Quelltexte, in dem die übliche HelloWorld-Anwendung als main.c enthalten ist:

#include <stdio.h>
#include <stdlib.h>

int main()
{
    printf("Hello world!\n");
    return 0;
}

Die Syntax für die inkludierten Bibliotheken ist in C etwas anders als in C++; auch für die Ausgabe werden jetzt andere Funktionen verwendet – hier zum Beispiel die Funktion printf().

In Kennenlernen der Entwicklungsumgebung Code::Blocks wurde empfohlen, die offline-Version der Dokumentation für C++ herunterzuladen; diese enthält auch die Dokumentation für C, in der sie die Beschreibungen der hier verwendeten Funktionen finden können.

Löst man den Build-Prozess aus, wird die Objekt-Datei erzeugt und man kann auf der Code::Blocks-Konsole sehen, welcher Compiler-Aufruf stattfindet.

Führt man das Programm anschließend aus, öffnet sich eine Konsole, in der Hello world! steht.

Möchte man weitere Quelltext-Dateien erzeugen, kann man mit

File -> New -> File ...

entweder Header-Dateien oder Quelltext-Dateien anlegen; letzere müssen jetzt die Datei-Endung .c besitzen.

In anderen Entwicklungsumgebungen sind die Schritte zum Anlegen eines neuen C-Projektes ähnlich.

Wenn in den weiteren Kapiteln Listings für C-Programme gezeigt werden, so sind diese – sofern nicht ausdrücklich eine main()-Funktion enthalten ist – folgendermaßen zu verstehen:

Man kann daher die Programme leicht testen, indem man sich von der Entwicklungsumgebung eine HelloWorld-Anwendung erzeugen lässt, die typischen Anweisungen für "HelloWorld" löscht und die Anweisungen aus dem Listing einfügt.