C-Programmierung: FAQ
Hinweis: Weitere Fragen bitte einfach in eine Kategorie oder ganz unten einfügen. Es müssen dabei keine Antworten mit angegeben werden.
Fragen zu diesem Buch
BearbeitenIch habe in einem anderen Buch gelesen, dass ...
BearbeitenIn 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 mitint main()
oderint main(void)
oderint main(int argc, char *argv[])
beginnen muss, auch wenn viele Compilervoid
dennoch akzeptieren.Die Definition mitmain()
war früher (C89/ANSI C) gültig, da beim Fehlen eines Rückgabetyps angenommen wurde, dass die Funktionint
zurückliefert. - Der Variablentyp xyz hat die Größe von xyz Byte. – Auch dies ist falsch. Der Standard legt für Basis-Datentypen (char,short,int,long,float,double...) lediglich Mindest-Wertebereiche fest, nur für
char
wird 1 Byte Typgröße vorgeschrieben. Mit dem C99-Standard wurden Typen wie beispielsweiseint8_t
oderint16_t
eingeführt, die eine feste (Bit)Größe besitzen. - Eine Variable des Typs
char
ist 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_t size = sizeof(datentyp) * CHAR_BIT;
- Ein Array zerfällt immer in einen Zeiger auf das erste Element – Der Standard beschreibt Ausnahmen, ebenso wie ein Array nie als LValue (linker Wert einer Zuweisung) verwendet werden kann.
Variablen und Konstanten
BearbeitenEs 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?
Bearbeiten
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.
Welche Größe hat der Typ int
auf einem 64 Bit Prozessor ?
Bearbeiten
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.
Es ist mir immer noch nicht ganz klar, was dieses EOF Zeichen bedeutet.
BearbeitenEOF 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.
Operatoren
BearbeitenMir 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.
Bearbeiten
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)!
Zeiger
BearbeitenIst 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.
Bearbeiten
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 automatisch bei Zuweisung auf jeden anderen Datenzeigertyp konvertiert wird, d.h. er ist immer zu jedem anderen Datenzeigertyp kompatibel.
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 2. Auflage die erste der beiden Varianten und behaupten, dass dieser Typ explizit in den gewünschten Typ umgewandelt werden muss; sie korrigieren dies aber im Errata zum Buch:
The remark about casting the return value of malloc ("the proper method is to declare ... then explicitly coerce") needs to be rewritten. The example is correct and works, but the advice is debatable in the context of the 1988-1989 ANSI/ISO standards. It's not necessary (given that coercion of void * to ALMOSTANYTYPE * is automatic), and possibly harmful if malloc, or a proxy for it, fails to be declared as returning void *. The explicit cast can cover up an unintended error. On the other hand, pre-ANSI, the cast was necessary, and it is in C++ also.
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.