Szukaj…


Uwagi

Zobacz także Rozmiar typów całkowych .

Rodzaje klas

Przez „klasę” rozumiemy typ, który został zdefiniowany przy użyciu słowa kluczowego class lub struct (ale nie enum struct ani enum class ).

  • Nawet pusta klasa nadal zajmuje co najmniej jeden bajt pamięci; będzie zatem składać się wyłącznie z wyściółki. Zapewnia to, że jeśli p wskazuje na obiekt pustej klasy, to p + 1 jest odrębnym adresem i wskazuje na odrębny obiekt. Możliwe jest jednak, że pusta klasa ma rozmiar 0, gdy zostanie użyta jako klasa podstawowa. Zobacz optymalizację pustej bazy .

    class Empty_1 {};                               // sizeof(Empty_1)       == 1
    class Empty_2 {};                               // sizeof(Empty_2)       == 1
    class Derived : Empty_1 {};                     // sizeof(Derived)       == 1
    class DoubleDerived : Empty_1, Empty_2 {};      // sizeof(DoubleDerived) == 1
    class Holder { Empty_1 e; };                    // sizeof(Holder)        == 1
    class DoubleHolder { Empty_1 e1; Empty_2 e2; }; // sizeof(DoubleHolder)  == 2
    class DerivedHolder : Empty_1 { Empty_1 e; };   // sizeof(DerivedHolder) == 2
    
  • Obiektowa reprezentacja typu klasy zawiera reprezentacje obiektowe klasy podstawowej i niestatycznych typów członków. Dlatego na przykład w następującej klasie:

    struct S {
        int x;
        char* y;
    };
    

    istnieje kolejna sekwencja bajtów sizeof(int) w obiekcie S , zwana podobiektem, która zawiera wartość x , oraz kolejny podobiekt o sizeof(char*) który zawiera wartość y . Tych dwóch nie można przeplatać.

  • Jeśli typ klasy ma elementy i / lub klasy podstawowe z typami t1, t2,...tN , rozmiar musi wynosić co najmniej sizeof(t1) + sizeof(t2) + ... + sizeof(tN) biorąc pod uwagę poprzednie punkty . Jednak w zależności od wymagań dotyczących wyrównania elementów i klas bazowych kompilator może zostać zmuszony do wstawienia wypełnienia między podobiektami lub na początku lub na końcu całego obiektu.

    struct AnInt      { int i; };
      // sizeof(AnInt)        == sizeof(int)
      // Assuming a typical 32- or 64-bit system, sizeof(AnInt)        == 4 (4).
    struct TwoInts    { int i, j; };
      // sizeof(TwoInts)      >= 2 * sizeof(int)
      // Assuming a typical 32- or 64-bit system, sizeof(TwoInts)      == 8 (4 + 4).
    struct IntAndChar { int i; char c; };
      // sizeof(IntAndChar)   >= sizeof(int) + sizeof(char)
      // Assuming a typical 32- or 64-bit system, sizeof(IntAndChar)   == 8 (4 + 1 + padding).
    struct AnIntDerived : AnInt { long long l; };
      // sizeof(AnIntDerived) >= sizeof(AnInt) + sizeof(long long)
      // Assuming a typical 32- or 64-bit system, sizeof(AnIntDerived) == 16 (4 + padding + 8).
    
  • Jeśli dopełnienie zostanie wstawione do obiektu ze względu na wymagania dotyczące wyrównania, rozmiar będzie większy niż suma rozmiarów elementów i klas podstawowych. Przy wyrównaniu n bajtów rozmiar będzie zwykle najmniejszą wielokrotnością n która jest większa niż rozmiar wszystkich elementów i klas podstawowych. Każdy członek memN będzie zazwyczaj umieszczony pod adresem będącym wielokrotnością alignof(memN) , a n będzie zazwyczaj największym alignof spośród wszystkich alignof członków. Z tego powodu, jeśli po elemencie o mniejszym alignof następuje element o większym alignof , istnieje możliwość, że ten drugi element nie zostanie właściwie wyrównany, jeśli zostanie umieszczony bezpośrednio za pierwszym. W takim przypadku wyściółka (znana również jako element wyrównujący ) zostanie umieszczona między dwoma elementami, tak że ten ostatni element może mieć pożądane ustawienie. I odwrotnie, jeśli po elemencie o większym alignof następuje element o mniejszym alignof , zwykle nie będzie konieczne wypełnianie. Ten proces jest również znany jako „pakowanie”.
    Ze względu na to, że klasy zwykle dzielą alignof swojego członka z największym alignof , klasy będą zazwyczaj wyrównane z alignof największego wbudowanego typu, który zawierają bezpośrednio lub pośrednio.

    // Assume sizeof(short) == 2, sizeof(int) == 4, and sizeof(long long) == 8.
    // Assume 4-byte alignment is specified to the compiler.
    struct Char { char c; };
      // sizeof(Char)                == 1 (sizeof(char))
    struct Int  { int i; };
      // sizeof(Int)                 == 4 (sizeof(int))
    struct CharInt { char c; int i; };
      // sizeof(CharInt)             == 8 (1 (char) + 3 (padding) + 4 (int))
    struct ShortIntCharInt { short s; int i; char c; int j; };
      // sizeof(ShortIntCharInt)     == 16 (2 (short) + 2 (padding) + 4 (int) + 1 (char) +
      //                                    3 (padding) + 4 (int))
    struct ShortIntCharCharInt { short s; int i; char c; char d; int j; };
      // sizeof(ShortIntCharCharInt) == 16 (2 (short) + 2 (padding) + 4 (int) + 1 (char) +
      //                                    1 (char) + 2 (padding) + 4 (int))
    struct ShortCharShortInt { short s; char c; short t; int i; };
      // sizeof(ShortCharShortInt)   == 12 (2 (short) + 1 (char) + 1 (padding) + 2 (short) +
      //                                    2 (padding) + 4 (int))
    struct IntLLInt { int i; long long l; int j; };
      // sizeof(IntLLInt)            == 16 (4 (int) + 8 (long long) + 4 (int))
      // If packing isn't explicitly specified, most compilers will pack this as
      //   8-byte alignment, such that:
      // sizeof(IntLLInt)            == 24 (4 (int) + 4 (padding) + 8 (long long) +
      //                                    4 (int) + 4 (padding))
    
    // Assume sizeof(bool) == 1, sizeof(ShortIntCharInt) == 16, and sizeof(IntLLInt) == 24.
    // Assume default alignment: alignof(ShortIntCharInt) == 4, alignof(IntLLInt) == 8.
    struct ShortChar3ArrShortInt {
        short s;
        char c3[3];
        short t;
        int i;
    };
      // ShortChar3ArrShortInt has 4-byte alignment: alignof(int) >= alignof(char) &&
      //                                             alignof(int) >= alignof(short)
      // sizeof(ShortChar3ArrShortInt) == 12 (2 (short) + 3 (char[3]) + 1 (padding) +
      //                                      2 (short) + 4 (int))
      // Note that t is placed at alignment of 2, not 4.  alignof(short) == 2.
    
    struct Large_1 {
        ShortIntCharInt sici;
        bool b;
        ShortIntCharInt tjdj;
    };
      // Large_1 has 4-byte alignment.
        // alignof(ShortIntCharInt) == alignof(int) == 4
        // alignof(b) == 1
        // Therefore, alignof(Large_1) == 4.
      // sizeof(Large_1) == 36 (16 (ShortIntCharInt) + 1 (bool) + 3 (padding) +
      //                        16 (ShortIntCharInt))
    struct Large_2 {
        IntLLInt illi;
        float f;
        IntLLInt jmmj;
    };
      // Large_2 has 8-byte alignment.
        // alignof(IntLLInt) == alignof(long long) == 8
        // alignof(float) == 4
        // Therefore, alignof(Large_2) == 8.
      // sizeof(Large_2) == 56 (24 (IntLLInt) + 4 (float) + 4 (padding) + 24 (IntLLInt))
    
C ++ 11
  • Jeśli ścisłe wyrównanie zostanie wymuszone za pomocą alignas , zostanie zastosowane wypełnienie, aby wymusić na typie osiągnięcie określonego wyrównania, nawet jeśli w innym przypadku byłby mniejszy. Na przykład, z poniższą definicją, w Chars<5> na końcu zostaną wstawione trzy (lub więcej) bajty wypełniające, tak że ich całkowity rozmiar to 8. Nie jest możliwe, aby klasa z wyrównaniem 4 miała rozmiar z 5, ponieważ nie byłoby możliwe utworzenie tablicy tej klasy, więc rozmiar należy „zaokrąglić w górę” do wielokrotności 4, wstawiając bajty dopełniania.

    // This type shall always be aligned to a multiple of 4.  Padding shall be inserted as
    // needed.
    // Chars<1>..Chars<4> are 4 bytes, Chars<5>..Chars<8> are 8 bytes, etc.
    template<size_t SZ>
    struct alignas(4) Chars { char arr[SZ]; };
    
    static_assert(sizeof(Chars<1>) == sizeof(Chars<4>), "Alignment is strict.\n");
    
  • Jeśli dwa niestatyczne elementy klasy mają ten sam specyfikator dostępu , to ten, który pojawia się później w kolejności deklaracji, gwarantuje, że pojawi się później w reprezentacji obiektu. Ale jeśli dwa elementy niestatyczne mają różne specyfikatory dostępu, ich względna kolejność w obiekcie jest nieokreślona.
  • Nie jest określone, w jakiej kolejności podobiekty klasy podstawowej pojawiają się w obiekcie, czy występują kolejno i czy pojawiają się przed, po, czy pomiędzy podobiektami składowymi.

Typy arytmetyczne

Wąskie typy znaków

Typ unsigned char używa wszystkich bitów do reprezentowania liczby binarnej. Dlatego na przykład, jeśli unsigned char ma długość 8 bitów, wówczas 256 możliwych wzorców bitowych obiektu char reprezentuje 256 różnych wartości {0, 1, ..., 255}. Gwarantowana liczba 42 jest reprezentowana przez wzór bitowy 00101010 .

signed char typ signed char nie ma bitów wypełniających, tzn. Jeśli signed char ma długość 8 bitów, wówczas ma 8 bitów pojemności do reprezentowania liczby.

Należy pamiętać, że te gwarancje nie dotyczą typów innych niż wąskie typy znaków.

Typy całkowite

Typy całkowite bez znaku używają czystego systemu binarnego, ale mogą zawierać bity wypełniające. Na przykład możliwe jest (choć mało prawdopodobne), że liczba całkowita unsigned int ma długość 64 bitów, ale może przechowywać tylko liczby całkowite od 0 do 2 32-1 włącznie. Pozostałe 32 bity to bity wypełniające, do których nie należy pisać bezpośrednio.

Podpisane typy liczb całkowitych używają systemu binarnego z bitem znaku i ewentualnie bitami wypełniającymi. Wartości należące do wspólnego zakresu typu liczby całkowitej ze znakiem i odpowiadającego typu liczby całkowitej bez znaku mają tę samą reprezentację. Na przykład, jeśli wzór bitowy 0001010010101011 unsigned short obiektu reprezentuje wartość 5291 , wówczas reprezentuje również wartość 5291 gdy jest interpretowany jako short obiekt.

Definiuje się implementację, czy używana jest reprezentacja uzupełnienia do dwóch, uzupełnienie do jednego lub wielkość znaku, ponieważ wszystkie trzy systemy spełniają wymaganie z poprzedniego akapitu.

Typy zmiennoprzecinkowe

Reprezentacja wartości typów zmiennoprzecinkowych jest zdefiniowana w implementacji. Najczęściej typy float i double są zgodne z IEEE 754 i mają długość 32 i 64 bity (więc na przykład float miałaby 23 bity precyzji, która następowałaby po 8 bitach wykładnikowych i 1 bicie znaku). Jednak standard niczego nie gwarantuje. Typy zmiennoprzecinkowe często mają „reprezentacje pułapek”, które powodują błędy, gdy są używane w obliczeniach.

Tablice

Typ tablicy nie ma wypełnienia między elementami. Dlatego tablica z typem elementu T jest po prostu sekwencją obiektów T ułożonych w pamięci, w kolejności.

Tablica wielowymiarowa jest tablicą tablic, a powyższe stosuje się rekurencyjnie. Na przykład, jeśli mamy deklarację

int a[5][3];

to a jest tablicą 5 tablic po 3 int . Dlatego a[0] , który składa się z trzech elementów: a[0][0] , a[0][1] , a[0][2] , jest umieszczony w pamięci przed a[1] , który składa się a[1][0] , a[1][1] i a[1][2] . Nazywa się to porządkiem głównym rzędu.



Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow