Speicherklassen und Deklarationen ================================= Diese Notizen enthalten vor allem ein Transkript der beigefügten Tafelbilder mit zusätzlichen Details und Korrekturen. Speicherklassen --------------- Speicherklassen beschreiben die Art des Speichers, davon unterscheidet die Sprache C die folgenden vier: (aus 1-speicherklassen.jpg) 1. automatischer Speicher - lokale Variablen ohne static oder _Thread_local - Lebensdauer: vom Beginn bis zum Ende eines Blockes - Schlüsselworte: auto, register 2. statischer Speicher - globale Variablen, lokale Variablen mit static, String-Literale - Lebensdauer: vom Beginn bis zum Ende des Programmes 3. allozierter Speicher - Speicher, der mit malloc() angefordert wird - Lebensdauer: vom malloc() bis zur Freigabe durch free() 4. Thread-lokaler Speicher - Variablen mit _Thread_local - Lebensdauer: von Beginn bis zum Ende eines Threads - Jeder Thread hat seine eigene Ausgabe der Variable Vereinfacht kann man sich vorstellen, dass automatischer Speicher auf dem Stack liegt, statischer und allozierter Speicher bilden den Heap, dieser wächst bei malloc()-Aufrufen entsprechend. Sichtbarkeit ------------ Bezeichner sind von dem Punkt an sichtbar (d.h. der Kompiler erkennt den Bezeichner wieder), wo sie zuerst deklariert worden sind. Die Sichtbarkeit ist unabhängig von Verlinkung und Speicherklasse. Die Sichtbarkeit eines Bezeichners endet mit dem Ende des Blockes, in dem er definiert wurde bzw. mit dem Ende der Quelltextdatei. Sind mehrere Bezeichner gleichen Namens deklariert, so ist der innerste Bezeichner sichtbar. Verlinkung ---------- Verlinkung beschreibt, inwiefern verschiedene Bezeichner gleichen Namens das gleiche Ding bezeichnen. Es gibt drei Arten von Verlinkung: * Alle Bezeichner gleichen Namens mit externer Verlinkung innerhalb eines Programmes bezeichnen das gleiche Ding. * Alle Bezeichner gleichen Namens mit interner Verlinkung innerhalb einer Quelltextdatei bezeichnen das gleiche Ding. * Alle Bezeichner gleiechen Namens ohne Verlinkung bezeichnen verschiedene Dinge. Deklarationen und Definitionen ------------------------------ Eine Deklaration macht einen Bezeichner und seine Eigenschaften dem Kompiler bekannt. Ein Bezeichner kann im gleichen Block / auf höchster Ebene (d.h. außerhalb von FUnktionen) beliebig oft deklariert werden, alle Deklarationen müssen aber äquivalent sein. Von der Deklaration (die lediglich bekannt macht) zu unterscheiden ist die Definition, die zusätzlich zur Deklaration die zum Bezeichner zugehörige Variable auch erstellt. Jeder Bezeichner kann höchstens einmal definiert werden, eine mehrfache Definition des gleichen Bezeichners ist unzulässig. Eine vorläufige Definition eines Bezeichners deklariert zunächst diesen. Wenn bis zum Ende der Quelltextdatei keine Definition des Bezeichners erfolgt, dann wird vom Kompiler implizit eine Definition eingefügt, die bezeichnete Variable wird dann mit 0 initialisiert. Deklarationssyntax ------------------ Wir unterscheiden die Deklarationssyntax auf höchster Ebene und jene in einem Block. Es wird jeweils die Deklaration mit den zugehörigen Schlüsselworten gezeigt, der Typ der Variable ist hier immer int, ist aber unerheblich. Die Deklarationssyntax für Funktionen funktioniert ein wenig anders. Zunächst Deklarationen auf höchster Ebene: (aus 2-global.jpg) 1. int a; // externe Verlinkung, Speicherklasse statisch, vorläufige Definition 2. extern int a; // externe Verlinkung, Speicherklasse statisch, nur Deklaration 3. int a = 1; extern int a = 1; // externe Verlinkung, Speicherklasse statisch, gleichzeitig Definition 4. static int a; // interne Verlinkung, Speicherklasse statisch, vorläufige Definition 5. static int a = 1; // interne Verlinkung, Speicherklasse statisch, gleichzeitig Definition 6. typedef int a; // a ist Alias für den Typen int In einem Block, Blöcke sind mit { ... } umschlossene Code-Teile: (aus 3-lokal.jpg) 7. auto int a; register int a; int a; auto int a = 1; register int a = 1; int a = 1; // keine Verlinkung, Speicherklasse automatisch, gleichzeitig Definition // von register-Variablen darf keine Adresse genommen werden 8. extern int a; // globale Verlinkung, Speicherklasse statisch, nur Deklaration // extern int a = 1; ist unzulässig 9. static int a; static int a = 1; // keine Verlinkung, Speicherklasse statisch, gleichzeitig Definition Das Schlüsselwort _Thread_local kann vor Deklarationen der Typen 1, 2, 3, 4, 5, 8, und 9 gestellt werden, um statt einer statischen Variable eine Thread-lokale Variable zu deklarieren. Deklarationen in Parameter listen haben immer die Form 7, das Schlüsselwort auto ist unzulässig. Typqualifizierer ---------------- Typqualifizierer können in einer Deklaration unmittelbar vor den Typnamen gestellt werden, um den Typen zu qualifizieren, d.h. besondere Eigenschaften kenntlich zu machen. (aus 4-typqualifizierer.jpg) - const - Wert kann nach Initialisierung nicht verändert werden. - alle Zeichenketten-Literalle (z.B. "hallo") sind implizit const - volatile - Der Compiler darf Zugriffe auf die Variable nicht wegoptimieren oder in Reihenfolge verändern. - volatile ist keine Lösung, wenn der Compiler fehlerhaften Code wegoptimiert. Besser den Code reparieren. - _Atomic - Zugriff erfolgt atomar, also in einem Rutsch. - Für nebenläufige Programmierung wichtig, erstmal nicht interessant. - restrict - Garantie, das nur über diesen Zeiger auf ein Objekt zugegriffen wird. - für Optimierung interessant - Betrachten wir später, wenn es um Zeiger geht.