Namensräume
Varianten
Aktionen

Speicherklassenspezifizierer

Von cppreference.com
< c‎ | Sprache

Definieren Sie Speicherdauer und Bindung von Objekten und Funktionen

  • auto - automatische Dauer und keine Bindung
  • register - automatische Dauer und keine Bindung; die Adresse dieser Variablen kann nicht genommen werden
  • static - statische Dauer und interne Bindung (es sei denn, auf Blockebene)
  • extern - statische Dauer und externe Bindung (es sei denn, bereits als intern deklariert)
  • _Thread_local(bis C23)thread_local(seit C23) - Thread-Speicherdauer
(seit C11)

Inhalt

[bearbeiten] Erklärung

Speicherklassenspezifizierer erscheinen in Deklarationen und zusammengesetzten Literalausdrücken(seit C23). Höchstens ein Spezifizierer darf verwendet werden, außer dass _Thread_local(bis C23)thread_local(seit C23) mit static oder extern kombiniert werden kann, um die Bindung anzupassen(seit C11). Die Speicherklassenspezifizierer bestimmen zwei unabhängige Eigenschaften der von ihnen deklarierten Namen: Speicherdauer und Bindung.

1) Der Spezifizierer auto ist nur für Objekte erlaubt, die auf Blockebene deklariert sind (außer in Parameterlisten von Funktionen). Er zeigt eine automatische Speicherdauer und keine Bindung an, was die Standardeinstellungen für diese Arten von Deklarationen sind.
2) Der Spezifizierer register ist nur für Objekte erlaubt, die auf Blockebene deklariert sind, einschließlich Parameterlisten von Funktionen. Er zeigt eine automatische Speicherdauer und keine Bindung an (was die Standardeinstellung für diese Arten von Deklarationen ist), weist jedoch den Optimierer zusätzlich an, den Wert dieser Variablen, wenn möglich, in einem CPU-Register zu speichern. Unabhängig davon, ob diese Optimierung stattfindet oder nicht, können Variablen, die als register deklariert sind, nicht als Argumente für den Adressoperator, nicht _Alignas(bis C23)alignas(seit C23)(seit C11) und register-Arrays nicht in Zeiger konvertiert werden.
3) Der Spezifizierer static legt sowohl die statische Speicherdauer fest (es sei denn, er wird mit _Thread_local kombiniert)(seit C11) als auch die interne Bindung (es sei denn, er wird auf Blockebene verwendet). Er kann mit Funktionen auf Dateiebene und mit Variablen auf Datei- und Blockebene verwendet werden, jedoch nicht in Parameterlisten von Funktionen.
4) Der Spezifizierer extern legt die statische Speicherdauer fest (es sei denn, er wird mit _Thread_local(bis C23)thread_local(seit C23) kombiniert)(seit C11) und die externe Bindung. Er kann mit Funktions- und Objektdeklarationen auf Datei- und Blockebene verwendet werden (ausgenommen Parameterlisten von Funktionen). Wenn extern bei einer erneuten Deklaration eines Bezeichners auftritt, der bereits mit interner Bindung deklariert wurde, bleibt die Bindung intern. Andernfalls (wenn die vorherige Deklaration extern war, keine Bindung hatte oder nicht im Gültigkeitsbereich liegt) ist die Bindung extern.
5) _Thread_local(bis C23)thread_local(seit C23) zeigt die Thread-Speicherdauer an. Sie kann nicht mit Funktionsdeklarationen verwendet werden. Wenn sie bei einer Objektdelkaration verwendet wird, muss sie bei jeder Deklaration desselben Objekts vorhanden sein. Wenn sie bei einer Deklaration auf Blockebene verwendet wird, muss sie entweder mit static oder extern kombiniert werden, um die Bindung zu bestimmen.
(seit C11)

Wenn kein Speicherklassenspezifizierer angegeben ist, sind die Standardeinstellungen

extern für alle Funktionen
extern für Objekte auf Dateiebene
auto für Objekte auf Blockebene

Für jede Struktur oder Union, die mit einem Speicherklassenspezifizierer deklariert wird, gilt die Speicherdauer (aber nicht die Bindung) rekursiv für ihre Member.

Funktionsdeklarationen auf Blockebene können extern oder gar keinen Spezifizierer verwenden. Funktionsdeklarationen auf Dateiebene können extern oder static verwenden.

Funktionsparameter können keine Speicherklassenspezifizierer außer register verwenden. Beachten Sie, dass static eine spezielle Bedeutung bei Funktionsparametern vom Array-Typ hat.

[bearbeiten] Speicherdauer

Jedes Objekt hat eine Eigenschaft namens Speicherdauer, die die Lebensdauer des Objekts begrenzt. Es gibt vier Arten von Speicherdauer in C:

  • automatische Speicherdauer. Der Speicher wird zugewiesen, wenn der Block, in dem das Objekt deklariert wurde, betreten wird, und freigegeben, wenn er auf irgendeine Weise verlassen wird (goto, return, Erreichen des Endes). Eine Ausnahme bilden VLAs; ihr Speicher wird zugewiesen, wenn die Deklaration ausgeführt wird, nicht beim Betreten des Blocks, und freigegeben, wenn der Gültigkeitsbereich des Blocks verlassen wird, nicht beim Verlassen des Blocks(seit C99). Wenn der Block rekursiv betreten wird, erfolgt eine neue Zuweisung für jede Rekursionsebene. Alle Funktionsparameter und nicht-static-Objekte auf Blockebene haben diese Speicherdauer, ebenso wie zusammengesetzte Literale, die auf Blockebene verwendet werden(bis C23)
  • statische Speicherdauer. Die Speicherdauer erstreckt sich über die gesamte Ausführung des Programms, und der im Objekt gespeicherte Wert wird nur einmal vor der main-Funktion initialisiert. Alle als static deklarierten Objekte und alle Objekte mit interner oder externer Bindung, die nicht als _Thread_local(bis C23)thread_local(seit C23) deklariert sind(seit C11) haben diese Speicherdauer.
  • Thread-Speicherdauer. Die Speicherdauer erstreckt sich über die gesamte Ausführung des Threads, in dem sie erstellt wurde, und der im Objekt gespeicherte Wert wird bei Start des Threads initialisiert. Jeder Thread hat sein eigenes, separates Objekt. Wenn der Thread, der den Ausdruck ausführt, der auf dieses Objekt zugreift, nicht der Thread ist, der seine Initialisierung ausgeführt hat, ist das Verhalten implementierungsabhängig. Alle als _Thread_local(bis C23)thread_local(seit C23) deklarierten Objekte haben diese Speicherdauer.
(seit C11)
  • Zugewiesene Speicherdauer. Der Speicher wird auf Anforderung mithilfe von Funktionen zur dynamischen Speicherzuweisung zugewiesen und freigegeben.

[bearbeiten] Bindung

Bindung bezieht sich auf die Fähigkeit eines Bezeichners (Variable oder Funktion), in anderen Gültigkeitsbereichen referenziert zu werden. Wenn eine Variable oder Funktion mit demselben Bezeichner in mehreren Gültigkeitsbereichen deklariert wird, aber nicht von allen referenziert werden kann, werden mehrere Instanzen der Variable generiert. Die folgenden Bindungen werden erkannt:

  • keine Bindung. Die Variable oder Funktion kann nur aus dem Gültigkeitsbereich referenziert werden, in dem sie sich befindet (Blockebene). Alle Variablen auf Blockebene, die nicht als extern deklariert sind, haben diese Bindung, ebenso wie alle Funktionsparameter und alle Bezeichner, die keine Funktionen oder Variablen sind.
  • interne Bindung. Die Variable oder Funktion kann aus allen Gültigkeitsbereichen in der aktuellen Übersetzungseinheit referenziert werden. Alle Variablen auf Dateiebene, die als static oder constexpr(seit C23) deklariert sind, haben diese Bindung, und alle Funktionen auf Dateiebene, die als static deklariert sind (statische Funktionsdeklarationen sind nur auf Dateiebene erlaubt).
  • externe Bindung. Die Variable oder Funktion kann aus jeder anderen Übersetzungseinheit im gesamten Programm referenziert werden. Alle Variablen auf Dateiebene, die nicht als static oder constexpr(seit C23) deklariert sind, haben diese Bindung, alle Funktionsdeklarationen auf Dateiebene, die nicht als static deklariert sind, alle Funktionsdeklarationen auf Blockebene und zusätzlich alle Variablen oder Funktionen, die als extern deklariert sind, haben diese Bindung, es sei denn, eine vorherige Deklaration mit interner Bindung ist an dieser Stelle sichtbar.

Wenn derselbe Bezeichner sowohl mit interner als auch mit externer Bindung in derselben Übersetzungseinheit vorkommt, ist das Verhalten undefiniert. Dies ist möglich, wenn tentative Definitionen verwendet werden.

[bearbeiten] Bindung und Bibliotheken

Deklarationen mit externer Bindung werden üblicherweise in Header-Dateien verfügbar gemacht, damit alle Übersetzungseinheiten, die die Datei #include-en, denselben Bezeichner referenzieren können, der woanders definiert ist.

Jede Deklaration mit interner Bindung, die in einer Header-Datei vorkommt, führt zu einem separaten und eigenständigen Objekt in jeder Übersetzungseinheit, die diese Datei einschließt.

Bibliotheksinterface, Header-Datei "flib.h"

#ifndef FLIB_H
#define FLIB_H
void f(void);              // function declaration with external linkage
extern int state;          // variable declaration with external linkage
static const int size = 5; // definition of a read-only variable with internal linkage
enum { MAX = 10 };         // constant definition
inline int sum (int a, int b) { return a + b; } // inline function definition
#endif // FLIB_H

Bibliotheksimplementierung, Quelldatei "flib.c"

#include "flib.h"
 
static void local_f(int s) {} // definition with internal linkage (only used in this file)
static int local_state;       // definition with internal linkage (only used in this file)
 
int state;                       // definition with external linkage (used by main.c)
void f(void) { local_f(state); } // definition with external linkage (used by main.c)

Anwendungscode, Quelldatei "main.c"

#include "flib.h"
 
int main(void)
{
    int x[MAX] = {size}; // uses the constant and the read-only variable
    state = 7;           // modifies state in flib.c
    f();                 // calls f() in flib.c
}

[bearbeiten] Schlüsselwörter

auto, register, static, extern, _Thread_local thread_local

[bearbeiten] Anmerkungen

Das Schlüsselwort _Thread_local wird normalerweise über das Makro thread_local verwendet, das in der Header-Datei <threads.h> definiert ist.

(bis C23)

Die Spezifizierer typedef und constexpr(seit C23) sind formell als Speicherklassenspezifizierer in der C-Sprachgrammatik aufgeführt, spezifizieren aber keine Speicherung.

Der Spezifizierer auto wird auch für die Typinferenz verwendet.

(seit C23)

Namen auf Dateiebene, die const und nicht extern sind, haben in C externe Bindung (als Standard für alle Deklarationen auf Dateiebene), in C++ jedoch interne Bindung.

[bearbeiten] Beispiel

#include <stdio.h>
#include <stdlib.h>
 
// static storage duration
int A;
 
int main(void)
{
    printf("&A = %p\n", (void*)&A);
 
    // automatic storage duration
    int A = 1;   // hides global A
    printf("&A = %p\n", (void*)&A);
 
    // allocated storage duration
    int* ptr_1 = malloc(sizeof(int));   // start allocated storage duration
    printf("address of int in allocated memory = %p\n", (void*)ptr_1);
    free(ptr_1);                        // stop allocated storage duration
}

Mögliche Ausgabe

&A = 0x600ae4
&A = 0x7ffefb064f5c
address of int in allocated memory = 0x1f28c30

[bearbeiten] Referenzen

  • C23-Standard (ISO/IEC 9899:2024)
  • 6.2.2 Bindungen von Bezeichnern (S. 35-36)
  • 6.2.4 Speicherdauern von Objekten (S. 36-37)
  • 6.7.1 Speicherklassenspezifizierer (S. 97-100)
  • C17-Standard (ISO/IEC 9899:2018)
  • 6.2.2 Bindungen von Bezeichnern (S. 29-30)
  • 6.2.4 Speicherdauern von Objekten (S. 30)
  • 6.7.1 Speicherklassenspezifizierer (S. 79)
  • C11-Standard (ISO/IEC 9899:2011)
  • 6.2.2 Bindungen von Bezeichnern (S. 36-37)
  • 6.2.4 Speicherdauern von Objekten (S. 38-39)
  • 6.7.1 Speicherklassenspezifizierer (S. 109-110)
  • C99-Standard (ISO/IEC 9899:1999)
  • 6.2.2 Bindungen von Bezeichnern (S. 30-31)
  • 6.2.4 Speicherdauern von Objekten (S. 32)
  • 6.7.1 Speicherklassenspezifizierer (S. 98-99)
  • C89/C90-Standard (ISO/IEC 9899:1990)
  • 3.1.2.2 Bindungen von Bezeichnern
  • 3.1.2.4 Speicherdauern von Objekten
  • 3.5.1 Speicherklassenspezifizierer

[bearbeiten] Siehe auch

C++ Dokumentation für Speicherklassenspezifizierer