Namensräume
Varianten
Aktionen

Objekte und Ausrichtung

Von cppreference.com
< c‎ | Sprache

C-Programme erstellen, zerstören, greifen auf Objekte zu und manipulieren sie.

Ein Objekt in C ist ein Bereich der Datenspeicherung in der Ausführungsumgebung, dessen Inhalt Werte darstellen kann (ein Wert ist die Bedeutung des Inhalts eines Objekts, wenn er als ein bestimmtes Typ interpretiert wird).

Jedes Objekt hat

  • Größe (ermittelbar mit sizeof)
  • Ausrichtungsanforderung(ermittelbar durch _Alignof(bis C23)alignof(seit C23))(seit C11)
  • Speicherdauer (automatisch, statisch, zugewiesen, thread-lokal)
  • Lebensdauer (gleich der Speicherdauer oder temporär)
  • effektiver Typ (siehe unten)
  • Wert (der unbestimmt sein kann)
  • optional, ein Bezeichner, der dieses Objekt bezeichnet.

Objekte werden durch Deklarationen, Zuweisungsfunktionen, Zeichenkettenliterale, Verbundliterale und durch Nicht-lvalue-Ausdrücke, die Strukturen oder Unions mit Array-Mitgliedern zurückgeben, erstellt.

Inhalt

[bearbeiten] Objektdarstellung

Mit Ausnahme von Bitfeldern bestehen Objekte aus zusammenhängenden Sequenzen von einem oder mehreren Bytes, die jeweils aus CHAR_BIT Bits bestehen und mit memcpy in ein Objekt vom Typ unsigned char[n] kopiert werden können, wobei n die Größe des Objekts ist. Der Inhalt des resultierenden Arrays wird als Objektdarstellung bezeichnet.

Wenn zwei Objekte die gleiche Objektdarstellung haben, vergleichen sie sich gleich (außer wenn es sich um Gleitkomma-NaNs handelt). Der Umkehrschluss gilt nicht: Zwei Objekte, die sich gleich vergleichen, können unterschiedliche Objektdarstellungen haben, da nicht jedes Bit der Objektdarstellung am Wert beteiligt sein muss. Solche Bits können für Auffüllung zur Erfüllung von Ausrichtungsanforderungen, für Paritätsprüfungen, zur Anzeige von Trap-Darstellungen usw. verwendet werden.

Wenn eine Objektdarstellung keinen Wert des Objekttyps darstellt, wird sie als Trap-Darstellung bezeichnet. Der Zugriff auf eine Trap-Darstellung auf andere Weise als durch Lesen über einen lvalue-Ausdruck vom Zeicherntyp ist undefiniertes Verhalten. Der Wert einer Struktur oder Union ist niemals eine Trap-Darstellung, auch wenn ein bestimmtes Mitglied eine ist.

Für Objekte vom Typ char, signed char und unsigned char ist jedes Bit der Objektdarstellung erforderlich, um an der Wertdarstellung teilzunehmen, und jedes mögliche Bitmuster stellt einen eindeutigen Wert dar (keine Auffüllung, Trap-Bits oder mehrere Darstellungen zulässig).

Wenn Objekte von Ganzzahltypen (short, int, long, long long) mehrere Bytes belegen, ist die Verwendung dieser Bytes implementierungsabhängig, aber die beiden dominierenden Implementierungen sind Big-Endian (POWER, Sparc, Itanium) und Little-Endian (x86, x86_64): eine Big-Endian-Plattform speichert das höchstwertige Byte an der niedrigsten Adresse des von der Ganzzahl belegten Speicherbereichs, eine Little-Endian-Plattform speichert das niederwertigste Byte an der niedrigsten Adresse. Details finden Sie unter Endianness. Siehe auch das Beispiel unten.

Obwohl die meisten Implementierungen keine Trap-Darstellungen, Auffüllbits oder mehrere Darstellungen für Ganzzahltypen zulassen, gibt es Ausnahmen; beispielsweise kann ein Wert eines Ganzzahltyps auf Itanium eine Trap-Darstellung sein.

[bearbeiten] Effektiver Typ

Jedes Objekt hat einen effektiven Typ, der bestimmt, welche lvalue-Zugriffe gültig sind und welche die strikten Aliasing-Regeln verletzen.

Wenn das Objekt durch eine Deklaration erstellt wurde, ist der deklarierte Typ dieses Objekts der effektive Typ des Objekts.

Wenn das Objekt durch eine Zuweisungsfunktion (einschließlich realloc) erstellt wurde, hat es keinen deklarierten Typ. Ein solches Objekt erwirbt einen effektiven Typ wie folgt:

  • Der erste Schreibvorgang auf dieses Objekt über ein lvalue, das einen anderen Typ als den Zeicherntyp hat, zu diesem Zeitpunkt wird der Typ dieses lvalues zum effektiven Typ dieses Objekts für diesen Schreibvorgang und alle nachfolgenden Lesezugriffe.
  • memcpy oder memmove kopiert ein anderes Objekt in dieses Objekt oder kopiert ein anderes Objekt als Array von Zeicherntyp in dieses Objekt, zu diesem Zeitpunkt wird der effektive Typ des Quellobjekts (falls vorhanden) zum effektiven Typ dieses Objekts für diesen Schreibvorgang und alle nachfolgenden Lesezugriffe.
  • Jeder andere Zugriff auf das Objekt ohne deklarierten Typ, der effektive Typ ist der Typ des für den Zugriff verwendeten lvalues.

[bearbeiten] Strikte Aliasing-Regeln

Gegeben sei ein Objekt mit effektivem Typ T1. Die Verwendung eines lvalue-Ausdrucks (typischerweise das Dereferenzieren eines Zeigers) eines anderen Typs T2 ist undefiniertes Verhalten, es sei denn:

  • T2 und T1 sind kompatible Typen.
  • T2 ist eine cvr-qualifizierte Version eines Typs, der kompatibel mit T1 ist.
  • T2 ist eine vorzeichenbehaftete oder vorzeichenlose Version eines Typs, der kompatibel mit T1 ist.
  • T2 ist ein Aggregattyp oder Union-Typ, der einen der oben genannten Typen unter seinen Mitgliedern enthält (einschließlich, rekursiv, eines Mitglieds eines Unteraggregats oder einer enthaltenen Union).
  • T2 ist ein Zeicherntyp (char, signed char oder unsigned char).
int i = 7;
char* pc = (char*)(&i);
 
if (pc[0] == '\x7') // aliasing through char is OK
    puts("This system is little-endian");
else
    puts("This system is big-endian");
 
float* pf = (float*)(&i);
float d = *pf; // UB: float lvalue *p cannot be used to access int

Diese Regeln steuern, ob der Compiler beim Kompilieren einer Funktion, die zwei Zeiger empfängt, Code ausgeben muss, der den einen nach dem Schreiben durch den anderen neu liest.

// int* and double* cannot alias
void f1(int* pi, double* pd, double d)
{
    // the read from *pi can be done only once, before the loop
    for (int i = 0; i < *pi; i++)
        *pd++ = d;
}
struct S { int a, b; };
 
// int* and struct S* may alias because S is an aggregate type with a member of type int
void f2(int* pi, struct S* ps, struct S s)
{
    // read from *pi must take place after every write through *ps
    for (int i = 0; i < *pi; i++)
        *ps++ = s;
}

Beachten Sie, dass der restrict-Qualifizierer verwendet werden kann, um anzuzeigen, dass zwei Zeiger nicht aliasen, auch wenn die obigen Regeln sie dazu zulassen.

Beachten Sie, dass Type-Punning auch über das inaktive Mitglied einer Union durchgeführt werden kann.

[bearbeiten] Ausrichtung

Jeder vollständige Objekttyp hat eine Eigenschaft, die als Ausrichtungsanforderung bezeichnet wird und ein Ganzzahlwert vom Typ size_t ist, der die Anzahl der Bytes zwischen aufeinanderfolgenden Adressen darstellt, an denen Objekte dieses Typs zugeordnet werden können. Die gültigen Ausrichtungswerte sind nicht-negative ganzzahlige Potenzen von zwei.

Die Ausrichtungsanforderung eines Typs kann mit _Alignof(bis C23)alignof(seit C23) abgefragt werden.

(seit C11)

Um die Ausrichtungsanforderungen aller Mitglieder einer Struktur zu erfüllen, können nach einigen ihrer Mitglieder Auffüllungen eingefügt werden.

#include <stdalign.h>
#include <stdio.h>
 
// objects of struct S can be allocated at any address
// because both S.a and S.b can be allocated at any address
struct S
{
    char a; // size: 1, alignment: 1
    char b; // size: 1, alignment: 1
}; // size: 2, alignment: 1
 
// objects of struct X must be allocated at 4-byte boundaries
// because X.n must be allocated at 4-byte boundaries
// because int's alignment requirement is (usually) 4
struct X
{
    int n;  // size: 4, alignment: 4
    char c; // size: 1, alignment: 1
    // three bytes padding
}; // size: 8, alignment: 4
 
int main(void)
{
    printf("sizeof(struct S) = %zu\n", sizeof(struct S));
    printf("alignof(struct S) = %zu\n", alignof(struct S));
    printf("sizeof(struct X) = %zu\n", sizeof(struct X));
    printf("alignof(struct X) = %zu\n", alignof(struct X));
}

Mögliche Ausgabe

sizeof(struct S) = 2
alignof(struct S) = 1
sizeof(struct X) = 8
alignof(struct X) = 4

Jeder Objekttyp legt seine Ausrichtungsanforderung für jedes Objekt dieses Typs fest. Die schwächste (kleinste) Ausrichtung ist die Ausrichtung der Typen char, signed char und unsigned char und entspricht 1. Die strengste (größte) fundamentale Ausrichtung eines Typs ist implementierungsabhängigund entspricht der Ausrichtung von max_align_t(seit C11).

Fundamentale Ausrichtungen werden für Objekte aller Arten von Speicherdauern unterstützt.

Wenn die Ausrichtung eines Objekts mithilfe von _Alignof(bis C23)alignof(seit C23) strenger (größer) als max_align_t gemacht wird, hat es eine erweiterte Ausrichtungsanforderung. Ein Struktur- oder Union-Typ, dessen Mitglied eine erweiterte Ausrichtung hat, ist ein übermäßig ausgerichteter Typ. Es ist implementierungsabhängig, ob übermäßig ausgerichtete Typen unterstützt werden, und ihre Unterstützung kann sich bei jeder Art von Speicherdauer unterscheiden.

Wenn ein Struktur- oder Union-Typ S kein Mitglied eines übermäßig ausgerichteten Typs oder mit einem Ausrichtungs-Spezifizierer deklariert hat, der eine erweiterte Ausrichtung angibt, hat S eine fundamentale Ausrichtung.

Die atomare Version jedes arithmetischen oder Zeigertyps hat eine fundamentale Ausrichtung.

(seit C11)

[bearbeiten] Fehlerberichte

Die folgenden verhaltensändernden Defect Reports wurden rückwirkend auf zuvor veröffentlichte C-Standards angewendet.

DR angewendet auf Verhalten wie veröffentlicht Korrigiertes Verhalten
DR 445 C11 ein Typ könnte eine erweiterte Ausrichtung haben, ohne dass _Alignas beteiligt ist er muss eine fundamentale Ausrichtung haben

[bearbeiten] Referenzen

  • C17-Standard (ISO/IEC 9899:2018)
  • 3.15 Objekt (S. 5)
  • 6.2.6 Darstellungen von Typen (S. 33-35)
  • 6.2.8 Ausrichtung von Objekten (S. 36-37)
  • 6.5/6-7 Ausdrücke (S. 55-56)
  • C11-Standard (ISO/IEC 9899:2011)
  • 3.15 Objekt (S. 6)
  • 6.2.6 Darstellungen von Typen (S. 44-46)
  • 6.2.8 Ausrichtung von Objekten (S. 48-49)
  • 6.5/6-7 Ausdrücke (S. 77)
  • C99-Standard (ISO/IEC 9899:1999)
  • 3.2 Ausrichtung (S. 3)
  • 3.14 Objekt (S. 5)
  • 6.2.6 Darstellungen von Typen (S. 37-39)
  • 6.5/6-7 Ausdrücke (S. 67-68)
  • C89/C90-Standard (ISO/IEC 9899:1990)
  • 1.6 Definitionen von Begriffen

[bearbeiten] Siehe auch

C++ Dokumentation für Objekt