C-Programmierung: FAQ
Aus Wikibooks
Hinweis: Weitere Fragen bitte einfach in eine Kategorie oder ganz unten einfügen. Es müssen dabei keine Antworten mit angegeben werden.
[Bearbeiten] Fragen zu diesem Buch
[Bearbeiten] Jedes Buch über C, das ich kenne, besitzt eine ASCII Tabelle. Nur dieses nicht. Warum das denn?
Vermutlich beschäftigen sich die Bücher, die du kennst, mit einer bestimmten Implementierung von C (häufig beschäftigen sich Bücher mit C unter DOS, Windows oder Linux). Wie bereits an mehreren Stellen des Buches erwähnt, legt sich der C Standard nicht auf einen bestimmten Zeichensatz fest.
[Bearbeiten] Ich habe in einem anderen Buch gelesen, dass ...
In diesem Fall solltest du hingehen und den Abschnitt verbessern. Allerdings gibt es eine ganze Reihe sehr populärer Irrtümer über C und du solltest deshalb vorher anhand des Standards überprüfen, ob die Aussage tatsächlich zutrifft. Hier nur ein unvollständige Liste der populärsten Irrtümer:
- Ein Programm beginnt mit
void main(void),main()usw. – Dies entspricht nicht dem (C99)-Standard. Dort ist festgelegt, dass jedes Programm (sofern ihm keine Parameter übergeben werde) mitint main()oderint main(void)beginnen muss. Die Definition mitvoid main()bzw.void main(void)ist kein gültiges C, da der Standard vorschreibt, dassmaineinen Rückgabewert vom Typintbesitzen muss (auch wenn viele Compilervoiddennoch akzeptieren). Die Definition mitmain()war früher gültig, da beim Fehlen eines Rückgabetyps angenommen wurde, dass die Funktionintzurückliefert. - Jeder C-Compiler besitzt eine Headerdatei mit dem Namen
stdio.h. – Dies ist falsch. Der Standard sagt ganz klar: A header is not necessarily a source file, nor are the<and>delimited sequence in header names necessarily valid source file names. (Abschnitt 7.1.2 Standard header Fußnote 154). Es muss also keine Datei mit dem Namenstdio.hgeben. Das Selbe trifft natürlich auch auf die anderen Headerdateien zu. - Der Variablentyp xyz hat die Größe von xyz Byte. – Auch dies ist falsch. Der Standard legt lediglich fest, dass
char1 Byte groß ist. Für die anderen Typen sind lediglich Mindestgrößen festgelegt. Erst mit dem C99-Standard wurden Variablen wie beispielsweiseint8_toderint16_teingeführt, die eine feste Größe besitzen. - Eine Variable des Typs
charist 8 Bit breit. – Auch dies ist genaugenommen nicht richtig. Einige Autoren behaupten dann noch, dass ein Byte in C eine beliebige Zahl von Bits haben kann. Richtig dagegen ist, dass in C ein Byte mindestens aus 8 Bit bestehen muss. Tatsächlich kann man dies aber häufig vernachlässigen; K&R erwähnen dies in ihrem Buch auch nicht gesondert. Wer dennoch hochportable Programme schreiben möchte und die Anzahl der Bits benötigt, kann dies wie folgt ermitteln:
#include <limits.h> // für CHAR_BIT size=sizeof(datentyp) * CHAR_BIT;
[Bearbeiten] Variablen und Konstanten
[Bearbeiten] Es heißt, dass der Ausdruck sizeof(char) immer den Wert 1 liefert, also der Typ char immer die Größe von 1 Byte hat. Dies ist aber unlogisch, da UNICODE Zeichen 16 Bit und damit 2 Byte besitzen. Hier widerspricht sich der Standard doch, oder?
Nein, tut er nicht. Der Denkfehler liegt darin anzunehmen, dass ein UNICODE Zeichen in einem char abgelegt werden muss. In der Regel wird es aber in einem wchar_t abgelegt. Dies ist laut C - Standard ein ganzzahliger Typ, dessen Wertebereich ausreicht, die Zeichen des größten erweiterten Zeichensatzes der Plattform aufzunehmen. Per Definition liefert sizeof(char) immer den Wert 1.
[Bearbeiten] Welche Größe hat der Typ int auf einem 64 Bit Prozessor ?
Der Standard legt die Größe des Typs int nicht fest. Er sagt nur: A "plain" int object has the natural size suggested by the architecture of the execution environment (Abschnitt 6.2.5 Types Absatz 4). Man könnte daraus schließen, dass int auf einer 64 Bit Plattform 64 Bit groß ist, was aber nicht immer der Fall ist.
[Bearbeiten] Es ist mir immer noch nicht ganz klar, was dieses EOF Zeichen bedeutet.
EOF ist ein negativer Integerwert, der von einigen Funktionen geliefert wird, wenn das Ende eines Stroms erreicht worden ist. Bei Funktionen, mit denen auf Dateien zugegriffen werden kann, ist EOF das End of File – also das Dateiende.
Der Denkfehler einiger Anfänger liegt vermutlich darin, dass EOF Zeichen grundsätzlich mit dem Dateiende gleichzusetzen. EOF kennzeichnet aber ganz allgemein das Ende eines Stroms, zu dem auch der Eingabestrom aus der Standardeingabe (Tastatur) gehört. Deshalb kann auch getchar EOF liefern.
[Bearbeiten] Operatoren
[Bearbeiten] Mir ist immer noch nicht ganz klar, warum a = i + i++ ein undefiniertes Resultat liefert. Der ++ - Operator hat doch eine höhere Priorität als der + -Operator.
Ja, es ist richtig, dass der ++ Operator eine höhere Priorität als der + -Operator hat. Der Compiler errechnet deshalb zunächst das Ergebnis von i++ . Allerdings müssen die Nebenwirkungen erst bis zum Ende des Ausdrucks ausgewertet worden sein.
Anders ausgedrückt: Der C-Standard schreibt dem Compiler nicht vor, wann er den Wert von i ändern muss. Dies kann sofort nach der Berechnung von i++ sein oder erst am Ende des Ausdrucks beim Erreichen des Sequenzpunktes.
Dagegen hat
b = c = 3; a = b + c++;
ein klar definiertes Ergebnis (nämlich 6), da die Variable c nicht an einer anderen Stelle vor dem Sequenzpunkt verändert wird.
Es ist etwas anders: Das Inkrement kennt zwei unterschiedliche Anwendungen, nämlich before (++i) und after (i++) bei der Durchführung, daher hat
b = c = 3; a = b + ++c;
auch ein klar definiertes Ergebnis (nämlich 7)!
[Bearbeiten] Zeiger
[Bearbeiten] Ist bei malloc ein Cast unbedingt notwendig? Ich habe schon öfter die Variante zeiger = (int*) malloc(sizeof(int) * 10); genauso wie zeiger = malloc(sizeof(int) * 10); gesehen.
Für diese Antwort muss man etwas ausholen: In der ersten Beschreibung der Sprache von K&R gab es noch keinen Zeiger auf void . Deshalb gab malloc einen char* zurück und ein cast auf andere Typen war notwendig. Es konnte deshalb nur die erste Variante zeiger = (int*) malloc(sizeof(int) * 10); eingesetzt werden.
Mit der Standardisierung von C wurde der untypisierte Zeiger void* eingeführt, der in jeden Typ gecastet werden kann. Daher ist kein expliziter Cast mehr notwendig und es kann die zweite Variante zeiger = malloc(sizeof(int) * 10); benutzt werden. K&R benutzen in ihrem Buch allerdings auch in der aktuellen Auflage die erste der beiden Varianten und behauptet, dass dieser Typ explizit in den gewünschten Typ umgewandelt werden muss. Dies ist aber einer der wenigen Fehler des Buches und wird vom ANSI C Standard nicht gefordert. Leider wird diese falsche Behauptung oft von vielen Büchern übernommen.
Es gibt allerdings dennoch einen Grund void* zu casten, und zwar dann wenn ein C++-Compiler zum Übersetzen benutzt werden soll. Da wir uns in diesem Buch allerdings an ANSI C halten, benutzen wir keinen Cast.