Bezeichner
Grundsätzlich können Namen in beliebiger Länge gebildet werden. Bei internen Namen sind alle Zeichen signifikant. Bei externen Namen wertet der Compiler standardmäßig maximal 32 Zeichen aus (siehe Regeln unten). Folgende Zeichen sind zur Bildung von Namen zulässig: Gemäß ANSI-/ISO-C die Ziffern 0 bis 9, die Großbuchstaben A bis Z, die Kleinbuchstaben a bis z und der Unterstrich _. Als Erweiterung gegenüber ANSI-/ISO-C sind standardmäßig auch das Dollarzeichen $ und das at-Zeichen @ in Namen zugelassen. Dies lässt sich mit entsprechenden Optionen ausschalten (-K no_dollar
, -K no_at
bzw. DOLLAR-ALLOWED=*NO, AT-ALLOWED=*NO).
Multibyte-Zeichen in Bezeichnern werden nicht unterstützt. Im Modus C11 können aber universal-character-names genutzt werden. Diese haben die Form \u0123 oder \U01234567. Nicht alle Ziffernfolgen sind erlaubt.
Für externe Namen gilt Folgendes:
Standardmäßig, d.h. die Optionen
-K c_names_std
bzw. C-NAMES=*STD sind gesetzt, können externe Namen maximal 32 Zeichen lang sein. Längere Namen werden vom Compiler auf 32 Stellen verkürzt.
Bei der Generierung von gemeinsam benutzbarem Code (Optionen-K share
bzw. SHAREABLE-CODE=*YES) können nur maximal 30 Zeichen genutzt werden.Bei Angabe der Optionen
-K c_names_unlimited
bzw. C-NAMES=*UNLIMITED findet keine Namensverkürzung statt. Der Compiler generiert Entry-Namen im EEN-Format. EEN-Namen können eine Länge von maximal 32000 Zeichen erreichen.Standardmäßig werden Kleinbuchstaben in Großbuchstaben und Unterstriche (_) in Dollarzeichen ($) übersetzt.
Durch die Angabe entsprechender Optionen (-K llm_case_lower
,-K llm_keep
bzw. LOWER-CASE-NAMES=*YES, SPECIAL-CHARACTERS=*KEEP) können Kleinbuchstaben und Unterstriche in den externen Namen beibehalten werden.Externe Namen dürfen nicht mit „
I
“ beginnen.- Enthält der Identifier universal-character-names, so werden diese als Zeichensequenz im externen Namen abgebildet. Dabei wird der Gegenschrägstrich durch ein Minus-Zeichen ersetzt. Der Buchstabe u bzw. U und die Ziffern bleiben erhalten.
Die obigen Regeln gelten auch für externe Namen, die in C++ als extern "C"
deklariert sind und außerdem für Static-Funktionen.
main-Funktion
Der Compiler lässt für die Funktion main
die Return-Typen int
und void
zu. Der Return-Typ void
führt immer zu einer Compiler-Meldung (meist eine Warnung).
Um einem Programm beim Aufruf Argumente übergeben zu können, sind für die Funktion main
zwei formale Parameter vorzusehen:
int main(int argc, char *argv[])
Der erste Parameter argc zeigt die Anzahl der übergebenen Argumente an. Da das erste Argument argv[0] gemäß Konvention der Programmname ist, ist die Argumentanzahl mindestens 1.
Der zweite Parameter argv ist ein Zeiger auf einen Vektor von Zeichenketten. In ihm werden der Programmname (in argv[0]) und alle beim Programmaufruf eingegebenen Argumente als mit dem Nullbyte (\0) abgeschlossene Zeichenketten abgespeichert.
Als Erweiterung gegenüber ANSI-/ISO-C kann für die Funktion main
ein dritter Parameter char *
envp[]
vereinbart werden (siehe "Erweiterungen gegenüber ANSI-/ISO-C").
Weitere Einzelheiten zur Übergabe von Parametern an die main
-Funktionen finden Sie im Abschnitt „Eingabe der Parameter für die main-Funktion“.
Zeichen (character)
Der Datentyp char
wird von diesem Compiler standardmäßig als unsigned
behandelt (siehe auch Optionen -K uchar
, -K schar
bzw. SIGNED-CHARACTER=*NO/*YES).
Der Wert eines EBCDIC-Zeichens ist immer positiv.
Der Wert von ’\377’ (oktal) oder ’\xFF’ (sedezimal) ist also 255.
Das Verhalten ist undefiniert, wenn eine character-Konstante einen numerischen Wert enthält, der nicht im EBCDIC-Zeichensatz enthalten ist.
Der Wert einer character-Konstante, die mehr als ein Zeichen enthält (z.B. ’ab’), berechnet sich aus dem EBCDIC-Wert der Zeichen als Zahl zur Basis 256. Das erste (rechte) Zeichen wird mit 1 multipliziert, das zweite Zeichen mit 256, das dritte Zeichen mit 256 * 256, das vierte Zeichen mit 256 * 256 * 256.
Z.B. ergibt ’abcd’ den Wert ’a’ * 2563 + ’b’ * 2562 + ’c’ * 256 + ’d’ (= 2172814212).
Der Wert einer Multibyte-Zeichenkonstante in der Form L’ab’ ist in dieser Implementierung identisch mit dem Wert einer Zeichenkonstante in der Form ’ab’.
Wenn eine character-Konstante fünf oder mehr Zeichen enthält, wird ein Error ausgegeben und kein Code generiert.
Die Zuweisung eines int
an char
geschieht modulo 256.
Multibytezeichen
In dieser Implementierung haben Multibytezeichen immer die Länge 1 Byte und wchar_t
-Werte sind immer Integer-Werte der Größe 32 bit.
Zeiger
Ein Zeiger wird in 4 Bytes dargestellt, mit Ausrichtung auf Wortgrenze. Die Differenz zwischen zwei Zeigern ist vom Typ int
(ptrdiff_t
).
Arrays
Arrays in C haben üblicherweise feste Grenzen, die Größe eines Arrays ist in solchen Fällen bereits zur Übersetzungszeit bekannt. Eine Ausnahme bilden die VLA (variable lenght array) aus C11. Diese können nur auf Block-Scope vorkommen.
Ein Arrayname wird in C immer wie ein Zeiger behandelt, der auf das erste Element des Arrays zeigt.
Die Elemente werden sequenziell im Speicher abgelegt, das erste Element hat den Index 0. Bei mehrdimensionalen Arrays werden die Elemente so im Speicher abgelegt, dass der letzte Index am schnellsten variiert. Jedes Element wird, wie das Array selbst, entsprechend dem Elementtyp ausgerichtet.
Strukturen
In Strukturen belegen die Komponenten Platz in der Reihenfolge ihrer Deklaration. Jede Komponente wird dabei entsprechend ihrem Typ ausgerichtet. Die Struktur selbst wird auf die maximal erforderliche Ausrichtungsgröße einer Komponente ausgerichtet. Die Strukturgröße ist ein Vielfaches dieser Ausrichtung, damit Arrays von diesen Strukturen gebildet werden können. Siehe auch „Interne Darstellung der Datentypen (Ausrichtung und Darstellung in Registern)“ und Präprozessor-Anweisung #pragma aligned
, "aligned-Pragma".
Beispiel
Größe: Ausrichtung: Offset: struct { char a; 1 Byte Bytegrenze 0 (Wortgrenze) short b; 2 Byte Halbwortgrenze 2 char c; 1 Byte Bytegrenze 4 long d; 4 Byte Wortgrenze 8 char e; 1 Byte Bytegrenze 12 }; 16 (Strukturende)
Bitfelder
Bitfelder werden von links nach rechts in maximal 64 Bit (Doppelwort) abgespeichert.
Bitfelder können folgendermaßen definiert sein:
int unsigned int signed int long unsigned long signed long long long unsigned long long signed long long short unsigned short signed short char unsigned char signed char
Bitfelder ohne den Zusatz unsigned
oder signed
werden entsprechend dem Basis-Datentyp dargestellt, d.h. char
als unsigned
und int
, long, long long
und short
als signed
. Bei expliziter Angabe von signed
oder unsigned
werden die Bitfelder entsprechend dieser Angabe dargestellt. Dieses voreingestellte Verhalten kann durch folgende Optionen verändert werden: -K schar
, -K signed_fields_unsigned
und -K plain_fields_unsigned
bzw. SIGNED-FIELDS=*UNSIGNED, PLAIN-FIELDS=*UNSIGNED, SIGNED-CHARACTER=*YES.
Die angegebene Anzahl von Bits wird ohne Ausrichtung angelegt, falls das Bitfeld noch ganz im aktuellen Byte, Halbwort, Wort oder Doppelwort Platz findet; andernfalls wird das Bitfeld entsprechend dem Grundtyp auf Byte-, Halbwort-, Wort- oder Doppelwortgrenze ausgerichtet (siehe Beispiel unten).
Beispiel
struct { unsigned short a : 7; unsigned short b : 5; unsigned short c : 5; unsigned short d : 8; } x;
Aufzählung (enum)
Ohne explizite Wertzuweisung werden den Konstanten bei der Definition eines Aufzählungstyps nacheinander die Zahlen 0, 1, etc. zugeordnet. Wird einer Konstanten ein Wert explizit zugewiesen, erhalten die nachfolgenden Konstanten automatisch einen entsprechend höheren Wert.
Standardmäßig wird ein Aufzählungstyp je nach den äußeren Grenzen (höchster und niedrigster Wert) dargestellt wie char
, short
oder long
. Mit den Compileroptionen -K enum_long
bzw. ENUM-TYPE=*LONG kann erreicht werden, dass enum-Daten unabhängig von ihrem tatsächlichen Platzbedarf stets als long
dargestellt werden.
Typqualifizierer volatile
volatile
verhindert die Optimierung beim Zugriff auf eine Variable. Anstelle der Verwendung des alten Inhalts wird stets neu aus dem Speicher eingelesen. Bei allen Zuweisungen, auch bei redundanten, wird der entsprechende Wert unmittelbar in den Speicher geschrieben. Es wird von der Implementierung garantiert, dass Referenzen auf volatile
-Objekte auf Werte zeigen, die im Speicher stehen, im Gegensatz zu nicht-volatile
-Objekten, die weitreichenden Optimierungen unterzogen und beispielsweise in Registern gehalten werden.
Im K&R-Modus wird volatile
nur syntaktisch akzeptiert.
size_t
size_t
entspricht in dieser Implementierung unsigned int
.
ptrdiff_t
ptrdiff_t
entspricht in dieser Implementierung int
.
Konvertierung von Datentypen
Integer --> Integer
Bei der Konvertierung eines vorzeichenlosen Integer-Werts in einen vorzeichenbehafteten Integer-Typ gleicher Größe wird das Bitmuster beibehalten. Wenn der Wert nicht aufgenommen werden kann, entspricht das Ergebnis der Subtraktion der größtmöglichen Zahl + 1 von der gegebenen Größe.
Wenn bei der Konvertierung eines Integer-Werts in einen kleineren Integer-Typ der Wert nicht aufgenommen werden kann, wird das Bitmuster beibehalten. Die höherwertigen Bits werden abgeschnitten.
Gleitkommazahl --> Integer
Bei der Konvertierung einer Gleitkommazahl nach Integer wird in Richtung 0 abgeschnitten.
Beispiel
(int)(-1.5) ist -1
(int)(1.5) ist 1
Das Ergebnis ist nicht definiert, wenn die zu konvertierende Gleitkommazahl zu groß ist, um als Integer-Wert dargestellt werden zu können.
Integer --> Gleitkommazahl
Bei der Konvertierung eines Integer- in einen Gleitkomma-Typ, der den korrekten Wert nicht aufnehmen kann, wird gerundet.
Gleitkommazahl --> Gleitkommazahl
Bei der Konvertierung einer Gleitkommazahl in eine kleinere Gleitkommazahl (z.B.
double
nachfloat
) wird gerundet.Integer <--> Zeiger
Bei der Konvertierung Integer nach Zeiger und umgekehrt wird das Bitmuster nicht verändert (einfache Uminterpretierung).
Vorzeichen des Divisionsrestes
Der Rest einer ganzzahligen Division hat immer dasselbe Vorzeichen wie der Dividend.
Beispiel
(-5) / 2 ist -2, (-5) % 2 ist -1
5 / (-2) ist -2, 5 % (-2) ist 1
Rechts-Shift logisch und arithmetisch
Rechts-Shift ist logisch (Auffüllen von 0-Bits), wenn der linke Operand unsigned
ist, sonst arithmetisch (Auffüllen von Vorzeichen-Bits).
Beispiel
(-8) >> 1 ist -4
Bitweise Operationen auf vorzeichenbehaftete Integerwerte
Bitweise Operationen (Operatoren ~, <<, &, ^, und |) werden bei Interpretation als vorzeichenlose Integer ausgeführt, das Ergebnis ist wieder vorzeichenbehaftet.
Deklaratoren
Für die Vereinbarung eines Typs sind beliebig viele Deklaratoren zugelassen.
switch-Anweisung
Pro switch
-Anweisung sind beliebig viele case
-Zweige zugelassen.
Präprozessor-Anweisungen
#include
Eine Folge von Include-Dateien <name> bzw. "name" ist nicht zulässig. Es wird nur der erste Name akzeptiert.
#include-Anweisungen, in denen die Namen der Include-Dateien Schrägstriche (/) für Verzeichnisse enthalten, werden vom Compiler auch im Falle von PLAM-Bibliothekselementen akzeptiert. Jeder Schrägstrich in den Namen von benutzereigenen und Standard-Include-Dateien wird intern zur Suche in PLAM-Bibliotheken in einen Punkt umgewandelt.
In Quellprogrammen, die z.B. aus dem POSIX- oder UNIX-System portiert werden, müssen deshalb die Schrägstriche nicht in Punkte umgewandelt werden.Beispiel
#include <sys/types.h>
Der Compiler sucht die Standard-Include-Datei SYS.TYPES.H in der CRTE-Bibliothek $.SYSLIB.CRTE.
Bezüglich der Schachtelung von Include-Dateien gibt es keine Einschränkungen.
#pragma
Siehe Abschnitt „Pragmas“.
__DATE__, __TIME__
Falls Datum und Uhrzeit der Übersetzung nicht verfügbar sind, sind diese Makros folgendermaßen definiert:
__DATE__
"Jan 1 1970"
__TIME__
"01:00:00"
Größe und Wertebereiche der elementaren Datentypen
Typ | Bit | Wertebereiche |
char | 8 | 0 .. 255 |
signed char | 8 | -128 .. 127 |
short | 16 | -32768 .. 32767 |
unsigned short | 16 | 0 .. 65535 |
int | 32 | -2147483648 .. 2147483647 (-231 .. 231-1) |
unsigned int | 32 | 0 .. 4294967295 (0 .. 232-1) |
long | 32 | wie int |
unsigned long | 32 | wie unsigned int |
long long | 64 | -9223372036854775808 .. 9223372036854775807 (-263.. 263-1) |
unsigned long long | 64 | 0 .. 18446744073709551615 (0 .. 264-1) |
float | 32 | 10-75 .. 0.79*1076 |
double | 64 | wie float |
long double | 124 | wie float |
Interne Darstellung der Datentypen (Ausrichtung und Darstellung in Registern)
Im Folgenden wird zusammenfassend gezeigt, wie die einzelnen C-Datentypen intern im Speicher abgebildet werden.
Bei skalaren Typen wird zusätzlich auf ihre Abbildung in Registern eingegangen. Dadurch wird zum einen festgelegt, wie die Variablen mit der Speicherklasse register
dargestellt werden, zum anderen, wie der Wert einer solchen Variablen in Ausdrücken interpretiert wird.
Datentyp | Größe | Ausrichtung | Darstellung in Registern |
char, unsigned char, | 1 Byte | Bytegrenze | rechtsbündig |
short, unsigned short | 2 Byte | Halbwortgrenze | rechtsbündig |
int, unsigned int | 4 Byte | Wortgrenze | wie im Speicher |
long, unsigned long | 4 Byte | Wortgrenze | wie im Speicher |
long long, unsigned long long | 8 Byte | Doppelwortgrenze | keine Darstellung in Registern |
Zeiger | 4 Byte | Wortgrenze | wie im Speicher |
float | 4 Byte | Wortgrenze | linksbündig |
double | 8 Byte | Doppelwortgrenze | Für Darstellung wird keine |
long double | 16 Byte | Doppelwortgrenze | Für Darstellung wird ein Gleit- |
Datentyp | Größe und Ausrichtung |
Aufzählung | Je nach den Grenzwerten dargestellt wie |
Arrays | Größe und Ausrichtung entsprechend dem Elementtyp. |
Strukturen | Größe und Ausrichtung für jede einzelne Komponente nach obigen |
Bitfelder | Die angegebene Anzahl von Bits wird ohne Ausrichtung angelegt, |
Implementierungsspezifische Grenzwerte
Die meisten Grenzwerte werden von den Systemressourcen vorgegeben (z.B. vom virtuellen Speicher). Nur die folgenden Grenzwerte sind von der Implementierung vorgegeben:
Merkmal | Maximalwert |
Anzahl der Parameter in einer Makrodefinition | 224-1 |
Anzahl der Argumente in einem Makroaufruf | 224-1 |
sizeof-Grenzwert | 231 |
Speicherklassen
In diesem Abschnitt wird zusammenfassend gezeigt, wie den Variablen in Abhängigkeit von ihrer Speicherklasse Speicher zugeordnet wird.
Speicherklasse register
Variablen können mit register
als Registervariablen deklariert werden. Dies ist ein Hinweis an den Compiler, dass die Variablen relativ oft benutzt werden und deshalb möglichst in Registern gehalten werden sollen. Beim Lesen und Schreiben dieser Variablen werden teure Speicherzugriffe eingespart. Die Optimierung des Compilers kann sich allerdings über diese Hinweise hinwegsetzen und nach einem eigenen Algorithmus bestimmte Variablen als Registervariablen realisieren.
Speicherklasse auto (default)
Für lokale Variablen mit der (voreingestellten) Speicherklasse auto
wird Speicher in einer Automatic Data Area reserviert.
Parameter in der Parameterliste
Funktionsparameter werden in der Reihenfolge ihres Auftretens in einer Parameterliste übergeben.
Alle unsigned
... Parameter werden wie unsigned
dargestellt, alle anderen ganzzahligen Parameter (char
, short
) wie int
: rechtsbündig in je einem Wort, ausgerichtet auf Wortgrenze und evtl. nach links aufgefüllt mit Vorzeichenbits (int
) oder Nullen (unsigned
...). Zeiger belegen ein Wort.
Abhängig vom Sprachmodus, werden Gleitkommazahlen unterschiedlich übergeben.
Im K&R-Modus werden Gleitkommazahlen (float
, double
) immer in doppelter Genauigkeit übergeben, also als Doppelwort ausgerichtet auf Doppelwortgrenze.
Im ANSI-Modus werden float
-Werte nur dann in doppelter Genauigkeit übergeben, wenn keine Prototyp-Deklaration vorhanden ist. Im anderen Fall werden float
-Werte in einfacher Genauigkeit übergeben, also als Wort ausgerichtet auf Wortgrenze.
In den C++-Sprachmodi werden float
-Werte immer in einfacher Genauigkeit übergeben, da Prototyp-Deklarationen vorhanden sein müssen.
long double
wird in zwei Doppelworten übergeben, ausgerichtet auf Doppelwortgrenze.
Strukturparameter werden je nach Bedarf auf Wort- oder Doppelwortgrenze ausgerichtet. Die Größe einer Struktur wird nach der maximal erforderlichen Ausrichtungsgröße einer Komponente aufgefüllt. Wenn z.B. eine Struktur nur short
- und char
-Komponenten enthält, ergibt sich als Größe ein Vielfaches von 2 Bytes.
Arrays können nicht als Wert übergeben werden. Es wird ein Zeiger auf das erste Array-Element übergeben.
Statische Variablen
Für die folgenden Arten von statischen Variablen reserviert der Compiler bereits bei der Übersetzung Speicher:
Lokale static-Variablen
Globale static-Variablen
Globale extern-Variablen
Der Unterschied zwischen diesen Speicherklassen liegt im Gültigkeitsbereich:
Lokale static-Variablen sind innerhalb einer Funktion mit dem Speicherklassenattribut
static
definierte Variablen. Sie sind nur der Funktion bekannt, in der sie definiert wurden.
Globale static-Variablen sind außerhalb einer Funktion mit dem Speicherklassenattribut
static
definierte Variablen. Diese sind nur innerhalb einer Übersetzungseinheit bekannt.Globale extern-Variablen sind Variablen, die außerhalb einer Funktion ohne das Speicherklassenattribut
static
definiert wurden. Auf diese Variablen kann auch in anderen Übersetzungseinheiten zugegriffen werden, wenn diese dort mit dem Attribut extern deklariert werden.
Funktionen ohne Prototyp
Wenn eine Funktion ohne Prototyp aufgerufen wird und es sind Parameter-Informationen vorhanden, wird in einigen Fällen ein Fehler ausgegeben. Dies geschieht, wenn eine Definition „alten Stils“ oder ein Prototyp im K&R-Modus vorgefunden werden.
Sind Argument und Parameter – nach der üblichen Typ-Erweiterung – verschiedenen Typs und einer der folgenden Punkte trifft zu, wird ein Fehler ausgegeben:
Parameter und Argument sind von unterschiedlicher Größe
Parameter und Argument sind von unterschiedlicher Ausrichtung
Der Parameter ist vom Typ float, double oder long double
Das Argument ist vom Typ float, double oder long double
Der Fehler kann zu einer Warnung herabgestuft werden. Wird dies getan, wird der Aufruf zur Laufzeit in aller Regel scheitern.