FAQ:ANSI/ISO C

[ FAQ in de.comp.lang.c ]


Diese FAQ bezieht sich in ihrer Gänze auf den inzwischen nicht mehr aktuellen ISO-C Standard 9899:1990, vielfach auch als C90 bezeichnet. Der seit Dezember 1999 existierende neue ISO 9899:1999 Standard (oder auch C99) wird nicht berücksichtigt.


[ Inhalt ][ Index ][ ZurÜck ][ Weiter ]


Frage 5.1: Was ist der "ANSI-C Standard?"

Antwort: 1983 rief das American National Standards Institute (ANSI) ein Komitee namens X3J11 ins Leben mit der Aufgabe, die Sprache C zu standardisieren. Nach langem, mühsamen Ringen um Definitionen und mehreren öffentlichen Überarbeitungen wurde die Arbeit dieses Komitees am 14. Dezember 1989 schliesslich als ANSI Standard X3.159-1989 verabschiedet und im Frühjahr 1990 veröffentlicht. Mehrheitlich wurde dabei einfach schon bestehende Gepflogenheiten standardisiert, mit einigen Anleihen bei C++ (z.B. Prototypen) sowie einem international angepassten Zeichensatz (eingeschlossen die umstrittenen Trigraphs). Der Standard definiert auch die C-Bibliotheken.

Der ursprüngliche ANSI Standard beinhaltete auch eine sogenannte "Rationale", also eine Erklärung, warum gewisse Entscheide so und nicht anders getroffen wurden. In dieser "Rationale" werden auch gewisse Feinheiten der Sprache C angesprochen die z.T. auch hier behandelt werden. Da diese "Rationale" nicht zum offiziellen ANSI Standard gehörte ("[it was] included for information only"), ist sie auch im ISO Standard nicht dabei.

Im Jahr 1990 wurde dieser Standard auch als Internationaler Standard akzeptiert (ISO/IEC 9899:1990), jedoch sind die Kapitel anders numeriert: ISO Kapitel 5 bis 7 sind Kapitel 2 bis 4 im ANSI Standard.

Anmerkung von TW::
Wie jeder ISO Standard wird auch der C Standard ständig überarbeitet. Bis heute gibt es zwei sogenannte "Technical Corrigenda", welche Unklarheiten im Standard korrigieren. Auch gibt es mittlerweile ein "Normative Addendum 1" (ca. 50 Seiten dick), welches den Standard erweitert (im wesentlichen um I/O von "wide chars" und "multibyte chars"). Zudem findet gerade eine Generalüberholung des Standards statt, die wohl grundlegende Änderungen mit sich bringen wird (Stichwort "C9X").


Frage 5.2: Wo kann ich eine Kopie des Standards bekommen?

Der ISO Standard wird durch ISO in Genf vertrieben:

	ISO Distribution
	Case Postale 56
	CH-1211 Geneve 20
	---------
	Suisse

(Da ISO unter anderem vom Verkauf von Standards lebt, gibt es keine Online-Version des Standards.)

In Herbert Schildts Buch "The Annotated C Standard" ist fast der ganze Text von ISO/IEC 9899:1990 enthalten (Osborne/McGraw-Hill, ISBN 0-07-881952-0).

Die "Rationale" ist via FTP von ftp.uu.net verfügbar: Verzeichnis doc/standards/ansi/X3.159-1989. Sie ist auch in gedruckter Form erhältlich von Silicon Press, ISBN 0-929306-07-4.


Frage 5.3: Hat jemand ein Programm, das C-Programme im alten Stil nach ANSI-C übersetzt und dabei automatisch Prototypen generiert?

Antwort: Die zwei Programme "protoize" und "unprotoize" konvertieren zwischen den beiden Arten von Funktionsdefinitionen und -deklarationen. (Sie können aber keine vollständige Übersetzung zwischen "klassischem" C und ANSI-C vornehmen.) Diese Programme sind Teil des gcc-Systems, man schaue im Verzeichnis pub/gnu auf prep.ai.mit.edu (18.71.0.38) oder jedem anderen FSF-Archiv nach.

Das Programm "unproto" (/pub/unix/unproto5.shar.Z auf ftp.win.tue.nl) ist ein Filter, der zwischen Präprozessor und dem Rest des Compilers aufgerufen wird und dabei die meisten ANSI-C-Konstrukte in traditionelles C übersetzt.

Im GNU "Ghostscript" gibt es ebenfalls ein kleines Programm namens ansi2knr.

Zu guter Letzt ist es eigentlich nicht nötig, einen Haufen alten Quelltext nach ANSI-C zu übersetzen: die traditionelle Syntax für Funktionen ist auch in ANSI-C noch gültig.

Siehe auch 5.8 und 5.9.


Frage 5.4: Ich will einen String generieren, der den Wert einer symbolischen Konstante enthält; dazu verwende ich den '#'-Operator des ANSI-C Präprozessors. Mein String enthält jedoch den Namen des Makros statt seines Wertes.

Antwort: Da beim Operanden des '#'-Operators keine Makro-Ersetzung gemacht wird, ist ein zweistufiges Vorgehen nötig, wenn der String aus der Ersetzung des Makros gebildet werden soll:

	#define Str(x)     #x
	#define Xstr(x)    Str(x)
	#define OP         plus

	char *opname = Xstr (OP);

Hier wird 'opname' auf "plus" gesetzt, nicht auf "OP".

Ein ähnlicher Zwischenschritt ist auch beim '##'-Operator nötig, wenn die Ersetzungen von Makros (statt ihrer Namen) zusammengesetzt werden sollen.

References: ANSI Sec. 3.8.3.2, Sec. 3.8.3.5 example; ISO Sec. 6.8.3.2, Sec. 6.8.3.5.


Frage 5.5: Warum kann ich keine "const"-Werte in Initialisierungen oder als Array-Dimensionen verwenden? Z.B.

	const int n = 5;
	int a[n];

Antwort: Der Typ-Qualifier "const" bedeutet eigentlich "read-only"; ein so qualifiziertes Objekt ist ein Laufzeit-Objekt, das nur gelesen werden kann. Der Wert eines solchen Objektes ist somit *kein* konstanter Ausdruck im eigentlichen Sinne. Wenn eine Konstante gebraucht wird, kann ein #define verwendet werden.

References: ANSI Sec. 3.4; ISO Sec. 6.4; H&S Secs. 7.11.2,7.11.3 pp. 226-7.


Frage 5.6: Was ist der Unterschied zwischen "const char *p" und "char * const p"?

Antwort: "char const *p" (oder eben auch "const char *p") deklariert einen Zeiger auf einen read-only Character, "char * const p" dagegen deklariert einen konstanten Zeiger auf einen (variablen) Character (d.h. der Zeiger kann nicht verändert werden, wohl aber das, worauf er zeigt).

References: ANSI Sec. 3.5.4.1 examples; ISO Sec. 6.5.4.1; Rationale Sec. 3.5.4.1; H&S Sec. 4.4.4 p. 81.


Frage 5.7: Warum kann ich keinen "char **" an ein Funktion übergeben, die einen "const char **" erwartet?

Antwort: Zwar kann man stets zeiger-auf-T (für alle Typen T) verwenden wo zeiger-auf-const-T erwartet wird. Diese Ausnahmeregel gilt aber nicht rekursiv!

Für Zuweisungen zwischen Zeigern, deren Qualifier sich auf einer anderen als der ersten Stufe unterscheiden, muss eine explizite Typkonvertierung (z.B. in diesem Fall "(const char **)") verwendet werden.

References: ANSI Sec. 3.1.2.6, Sec. 3.3.16.1, Sec. 3.5.3; ISO Sec. 6.1.2.6, Sec. 6.3.16.1, Sec. 6.5.3; H&S Sec. 7.9.1 pp. 221-222.


Frage 5.8: Mein ANSI Compiler reklamiert darüber:

	extern int func (float);

	int func (x)
	  float x;
	{...

Antwort: Hier wurde die Deklaration im ANSI-Stil mit einer Definition im alten Stil vermengt. Das ist zwar meistens in Ordnung (siehe 5.3), nicht aber in diesem Fall.

C nach alten Stil "erweitert" gewisse Argumente bei der Übergabe von Parameten: float wird zu double, und char und short int werden zu int. Innerhalb der Funktion wird dann eine Rückumwandlung auf den "kleineren" Typen vorgenommen. (ANSI C führt diese Erweiterung ebenfalls durch, wenn kein Prototyp sichtbar ist oder in variabel langen Argumentlisten, jedoch gibt es keine automatische Rück-konvertierung.)

Das Problem kann gelöst werden, indem entweder eine Definition nach ANSI verwendet wird:

	int func (float x)
	{...

oder der Prototyp der Definition nach altem Stil angepasst wird:

	extern int func (double);

(In diesem Fall wäre es aber wohl sauberer, auch die Definition so zu ändern, dass sie auch double verwendet, sofern nicht die Adresse des Parameters genommen wird.)

Eine andere Alternative ist natürlich, "kleine" Argumenttypen (char, short int und float) ganz zu vermeiden.

References: K&R1 Sec. A7.1 p. 186; K&R2 Sec. A7.3.2 p. 202; ANSI Sec. 3.3.2.2, Sec. 3.5.4.3; ISO Sec. 6.3.2.2, Sec. 6.5.4.3; Rationale Sec. 3.3.2.2, Sec. 3.5.4.3; H&S Sec. 9.2 pp. 265-7, Sec. 9.4 pp. 272-3.


Frage 5.9: Kann man Funktionsdeklarationen und -definitionen nach altem und neuem Stil mischen? Ist der alte Stil noch erlaubt?

Antwort: Der alte Stil ist noch erlaubt, und eine Mischung ist möglich. Dabei sollte man aber äusserst umsichtig sein, da es sonst zu unerwünschten Effekte kommen kann (siehe 5.3). Zu beachten ist auch, dass der alte Stil im ISO-Standard als "überholt" klassifiziert ist und somit in Zukunft vielleicht nicht mehr unterstützt werden wird.

References: ANSI Sec. 3.7.1, Sec. 3.9.5; ISO Sec. 6.7.1, Sec. 6.9.5; H&S Sec. 9.2.2 pp. 265-7, Sec. 9.2.5 pp. 269-70.


Frage 5.10: Weshalb erhalte ich bei

	extern f (struct x {int s;} *p);

eine Warnung "struct x introduced in prototype scope" oder so ähnlich?

Antwort: Das ist eine Anomalie der Scope-Regeln von C. Eine struct, die zum ersten Mal in einem Prototypen vorkommt (ohne vorher schon deklariert zu sein) kann zu keiner anderen struct kompatibel sein, da ihr Scope am Ende des Prototyps endet.

Das Problem kann behoben werden, indem die unvollständige Deklaration

	struct x;

auf Datei-Ebene vor dem Prototypen gemacht wird. Damit können alle folgenden Deklarationen, die struct x verwenden, sich auf den selben Typen beziehen.

References: ANSI Sec. 3.1.2.1, Sec. 3.1.2.6, Sec. 3.5.2.3; ISO Sec. 6.1.2.1, Sec. 6.1.2.6, Sec. 6.5.2.3.


Frage 5.11: Mein Compiler gibt komische Fehlermeldungen in Zeilen, die durch eine #ifdef-Direktive ausgeschlossen sind!

Antwort: In ANSI-C muss auch Text, der durch eine #ifdef-Direktive ausgeschaltet ist, aus gültigen Präprozessor-Tokens bestehen. Das bedeutet unter anderem, dass keine Zeilenumbrüche innerhalb von Anführungszeichen (Strings) vorkommen dürfen, und dass Apostrophen und Anführungszeichen immer paarweise auftreten müssen. Deshalb sollten Kommentare und Pseudo-Code immer durch /* und */ geklammert werden, nicht mittels #ifdef. Siehe aber auch 17.14.

Anmerkung von TW::
Der folgende Ausschnitt z.B. wird eine Fehlermeldung auslösen:

	#if 0
		Dies ist Hans' Lösung...
	#endif
	

References: ANSI Sec. 2.1.1.2, Sec. 3.1; ISO Sec. 5.1.1.2, Sec. 6.1; H&S Sec. 3.2 p. 40.


Frage 5.12: Kann ich main() als void deklarieren, um diese störenden Warnungen "main returns no value" zu umgehen? (Ich rufe exit() auf, also gibt es gar kein Return von main.)

Antwort: Nein. main() muss mit Rückgabewert int deklariert sein, und hat entweder keine oder aber genau zwei Argumente (und diese müssen dann die richtigen Typen haben). Falls exit() aufgerufen wird, so muss vielleicht eine unnütze 'return' - Anweisung eingefügt werden.

Eine Funktion als void zu deklarieren vermeidet nicht nur gewisse Warnungen, es kann auch in ganz anderem Code für Aufruf oder Return resultieren, meist nicht kompatibel mit dem, was der Aufrufer (im Falle von main() ist das der Startup-Code) erwartet.

References: ANSI Sec. 2.1.2.2.1, Sec. F.5.1; ISO Sec. 5.1.2.2.1, Sec. G.5.1; H&S Sec. 20.1 p. 416; CT&P Sec. 3.10 pp. 50-51.


Frage 5.13: Ist 'exit(status)' wirklich das Gleiche wie die Rückgabe eines Wertes von 'main'?

Antwort: Jein. Der Standard definiert sie als äquivalent. Einige ältere, nicht dem Standard entsprechende Implementationen können mit der einen oder anderen Form Probleme haben. Und natürlich sind die beiden Formen nicht das Selbe, wenn 'main' rekursiv aufgerufen wird.

References: K&R2 Sec. 7.6 pp. 163-4; ANSI Sec. 2.1.2.2.3; ISO Sec. 5.1.2.2.3.


Frage 5.14: Warum garantiert der Standard nicht mehr als 6 signifikante Zeichen (Gross- und Kleinschreibung ignoriert!) für externe Bezeichner?

Antwort: Das Problem sind ältere Linker, über die weder der Standard noch die Compiler-Entwickler irgendeine Kontrolle haben. Die Begrenzung ist nur auf 6 *signifikante* Zeichen, d.h. der volle Bezeichner kann sehr wohl länger sein.

Diese Konzession gegenüber restriktiven Linkern musste einfach gemacht werden, auch wenn viele damit nicht einverstanden waren. (Die Rationale erwähnt, diese Entscheidung sei "most painful" gewesen.) Falls Sie nicht einverstanden sind oder glauben, eine Methode entwickelt zu haben, diese Beschränkung zu umgehen, lesen Sie Abschnitt 3.1.2 in der X3.159 Rationale (siehe 5.1), wo verschiedene Möglichkeiten vorgeschlagen und verworfen werden.

References: ANSI Sec. 3.1.2, Sec. 3.9.1; ISO Sec. 6.1.2, Sec. 6.9.1; Rationale Sec. 3.1.2; H&S Sec. 2.5 pp. 22-3.


Frage 5.15: Was ist der Unterschied zwischen 'memmove()' und 'memcpy()'?

Antwort: Der Standard garantiert, dass 'memmove()' auch dann korrekt funktioniert, wenn sich die beiden Speicherbereiche überlappen. Für 'memcpy()' gibt es keine solche Garantie, es kann deshalb etwas effizienter implementiert werden. Im Zweifelsfalle sollte 'memmove()' verwendet werden.

References: K&R2 Sec. B3 p. 250; ANSI Sec. 4.11.2.1, Sec. 4.11.2.2; ISO Sec. 7.11.2.1, Sec. 7.11.2.2; Rationale Sec. 4.11.2; H&S Sec. 14.3 pp. 341-2; PCS Sec. 11 pp. 165-166.


Frage 5.16: Mein Compiler weigert sich, auch nur die allersimpelsten winzigen Progrämmchen zu übersetzen.

Antwort: Vielleicht ist es ein alter Compiler, der noch kein ANSI-C versteht: keine Prototypen von Funktionen und solche Dinge.

Siehe auch 5.17 und 17.2.


Frage 5.17: Wieso werden manche Funktionen aus der ANSI/ISO-Standard-Bibliothek als "undefiniert" angezeigt, obwohl ich einen ANSI-kompatiblen Compiler habe?

Antwort: Es ist sehr wohl möglich, zwar einen ANSI-kompatiblen Compiler zu haben, nicht aber eine ANSI-kompatible Bibliothek (und ebensolche Headerfiles). Das kommt insbesondere mit gcc häufig vor. Siehe auch 5.16 und 17.2.


Frage 5.18: Wieso akzeptiert mein Compiler, der angeblich ANSI-konform ist, diesen Code nicht? Ich weiss, dass der Code selbst ANSI-konform ist, denn gcc akzeptiert ihn.

Antwort: Viele Compiler implementieren ein paar nicht standardgemässe Erweiterungen, gcc mehr als viele andere. Wird im Code wirklich keine solche Erweiterung benutzt? Generell ist es keine gute Idee, mit Compilern zu experimentieren, um die Charakteristiken der Sprache zu ergründen - der Standard erlaubt vielleicht Unterschiede, oder der Compiler hat Fehler. Siehe auch 4.4.

Anmerkung von TW::
Übrigens kann auch gcc Fehler beinhalten - gcc ist keine Referenz-Implementierung!


Frage 5.19: Warum ist mit 'void *'-Zeigern keine Zeiger-Arithmetik möglich?

Antwort: Weil der Compiler die Grösse des Objektes, auf das gezeigt wird, nicht kennt. Erst nach einer Umwandlung auf 'char *' bzw. auf den Zeiger-Typ, der wirklich manipuliert werden soll, ist Arithmetik mit dem Zeiger möglich. (Siehe jedoch auch 2.18.)

References: ANSI Sec. 3.1.2.5, Sec. 3.3.6; ISO Sec. 6.1.2.5, Sec. 6.3.6; H&S Sec. 7.6.2 p. 204.


Frage 5.20: Ist char a[3] = "abc"; erlaubt? Was bedeutet das?

Antwort: Das ist in ANSI-C erlaubt, allerdings nur selten nützlich. Es wird ein Array mit 3 Elementen deklariert, das dann mit den drei Zeichen 'a', 'b' und 'c' initialisiert wird, ohne das sonst übliche '\0'-Zeichen am Ende! Das Array enthält also keinen String und kann somit *nicht* mit 'strcpy', 'printf %s' etc. verwendet werden.

Anmerkung von TW::
Nebenbei bemerkt sagt der Standard nichts darüber aus, was mit den Elementen 4 .. 9 in folgender Deklaration zu geschehen hat:

	char a[10] = "abc";

Das in 5.1 erwähnte "Technical Corrigendum 2" präzisiert, dass die Elemente 4 bis 9 ausgenullt werden müssen (egal, ob 'a' static, extern oder automatic ist).

References: ANSI Sec. 3.5.7; ISO Sec. 6.5.7; H&S Sec. 4.6.4 p. 98.


Frage 5.21: Was sind #pragmas und wozu sind sie gut?

Antwort: Die #pragma-Direktive stellt eine wohldefinierte Schnittstelle zur Verfügung, die der Compiler für alle Arten von selbstdefinierten, implementations-spezifischen Kontrollen und Erweiterungen verwenden kann, z.B. Optimierungen, "Packen" von structs, Unterdrückung von Warnungen, etc.

References: ANSI Sec. 3.8.6; ISO Sec. 6.8.6; H&S Sec. 3.7 p. 61.


Frage 5.22: Was bedeutet "#pragma once"? Das kommt in einigen Headerfiles vor.

Antwort: Manche Compiler stellen dieses Pragma zur Verfügung, um Headerfiles idempotent zu machen. "#pragma once" ist mehr oder weniger das Gleiche wie der #ifndef-Trick in 6.4.


Frage 5.23: Anscheinend nehmen einige Leute die Unterschiede zwischen "undefined" (undefiniertem), "unspecified" (nicht spezifiziertem) und "implementation-defined" (durch den Compiler definiertem) Verhalten ziemlich ernst. Was ist der Unterschied?

Antwort: Kurz gesagt: "implementation-defined" bedeutet, dass der Compiler eine Möglichkeit auswählen muss, und diese auch dokumentiert sein muss. "Unspecified" heißt, der Compiler sollte eine Möglichkeit wählen, die aber nicht dokumentiert sein braucht. "Undefined" schliesslich bedeutet, dass irgendetwas passieren kann. In keinem dieser Fälle legt der Standard irgendwelche Richtlinien fest, in den zwei ersten Fällen wird manchmal eine Auswahl möglicher Verhaltensweisen vorgeschlagen, wovon der Compiler eventuell eine zu wählen hat.

Wenn ein Programm portabel sein soll, können diese Unterscheidungen getrost vergessen werden: Code, der von obigen Verhaltensweisen abhängt, ist nicht portabel.

References: ANSI Sec. 1.6; ISO Sec. 3.10, Sec. 3.16, Sec. 3.17; Rationale Sec. 1.6.

[ Inhalt ][ Index ][ ZurÜck ][ Weiter ]


[ FAQ Logo ]   © 1997-2004 Jochen Schoof (joscho@bigfoot.de)
Diese Version wurde am 14. März 2004 erzeugt. Sie wird zukünftig nicht weiter gepflegt.