Spec-Zone.ru › C

Объекты и выравнивание

Программы на C создают, уничтожают, обращаются к и манипулируют объектами.

Объект в C — это область хранения данных в среде выполнения, содержимое которой может представлять значения (значение — это смысл содержимого объекта, когда оно интерпретируется как имеющее определённый тип).

Каждый объект имеет

  • размер (можно определить с помощью sizeof)
  • требования к выравниванию(можно определить с помощью _Alignof)(с C11)
  • срок хранения (автоматический, статический, выделенный, локальный для потока)
  • срок жизни (равный сроку хранения или временный)
  • эффективный тип (см. ниже)
  • значение (которое может быть неопределенным)
  • необязательно, идентификатор, который обозначает этот объект.

Объекты создаются объявлениями, функциями выделения памяти, строковыми литералами, сложными литералами и не-lvalue выражениями, которые возвращают структуры или объединения с массивами членов.

Представление объекта

За исключением бит-полей, объекты состоят из непрерывных последовательностей одного или нескольких байтов, каждый из которых состоит из CHAR_BIT бит, и могут быть скопированы с помощью memcpy в объект типа unsigned char[n], где n — размер объекта. Содержимое полученного массива известно как представление объекта.

Если два объекта имеют одинаковое представление объекта, они равны (кроме случаев, когда они являются плавающей точкой NaN). Обратное неверно: два равных объекта могут иметь разные представления объекта, потому что не каждый бит представления объекта должен участвовать в значении. Такие биты могут использоваться для заполнения, чтобы удовлетворить требованиям выравнивания, для проверок чётности, для обозначения представлений ловушки и т. п.

Если представление объекта не представляет никакого значения типа объекта, оно известно как представление ловушки. Доступ к представлению ловушки любым способом, кроме чтения его через выражение lvalue типа символьного типа, является неопределённым поведением. Значение структуры или объединения никогда не является представлением ловушки, даже если любой конкретный член является таковым.

Для объектов типа char, signed char и unsigned char каждый бит представления объекта должен участвовать в представлении значения, и каждая возможная битовая комбинация представляет собой отличное значение (без заполнения, битов ловушки или множественных представлений).

Когда объекты целочисленных типов (short, int, long, long long) занимают несколько байтов, использование этих байтов определяется реализацией, но два доминирующих варианта реализации — big-endian (POWER, Sparc, Itanium) и little-endian (x86, x86_64): платформа big-endian хранит наиболее значимый байт в наименьшем адресе области памяти, занимаемой целым числом, платформа little-endian хранит наименее значимый байт в наименьшем адресе. Подробнее см. Endianness. См. также пример ниже.

Хотя большинство реализаций не допускают представлений ловушек, заполняющих битов или множественных представлений для целочисленных типов, есть исключения; например, значение целочисленного типа на Itanium может быть представлением ловушки.

Эффективный тип

Каждый объект имеет эффективный тип, который определяет, какие lvalue доступы допустимы, а какие нарушают правила строгого алиасинга.

Если объект был создан объявлением, тип, объявленный для этого объекта, является эффективным типом объекта.

Если объект был создан функцией выделения памяти (включая realloc), он не имеет объявленного типа. Такой объект приобретает эффективный тип следующим образом:

  • Первое запись в этот объект через lvalue, имеющий тип, отличный от символьного типа, в момент, когда тип этого lvalue становится эффективным типом этого объекта для этой записи и всех последующих чтений.
  • memcpy или memmove копируют другой объект в этот объект, или копируют другой объект в этот объект как массив символьного типа, при этом эффективный тип исходного объекта (если он был) становится эффективным типом этого объекта для этой записи и всех последующих чтений.
  • Любой другой доступ к объекту без объявленного типа, эффективный тип — тип lvalue, используемого для доступа.

Строгий алиасинг

Учитывая объект с эффективным типом T1, использование выражения lvalue (обычно, разыменование указателя) другого типа T2 является неопределённым поведением, за исключением случаев:

  • T2 и T1 — совместимые типы.
  • T2 — версия типа с cvr-квалификатором, которая совместима с T1.
  • T2 — знаковый или беззнаковый вариант типа, который совместим с T1.
  • T2 — агрегатный или объединяющий тип, который включает один из упомянутых типов среди своих членов (включая, рекурсивно, член под-агрегата или содержащего объединение).
  • T2 — тип символьного типа (char, signed char или 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

Эти правила управляют тем, должна ли функция, принимающая два указателя, перечитывать один после записи через другой:

// 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;
}

Обратите внимание, что квалификатор restrict может использоваться для указания того, что два указателя не являются алиасами, даже если вышеуказанные правила позволяют это.

Обратите внимание, что манипуляции с типом также могут выполняться через неактивный член объединения.

Выравнивание

Каждый полный тип объекта имеет свойство, называемое требованием выравнивания, которое представляет собой целочисленное значение типа size_t , представляющее количество байтов между последовательными адресами, в которых могут быть выделены объекты этого типа. Допустимые значения выравнивания — это целые неотрицательные степени двойки.

Требование выравнивания типа можно запросить с помощью _Alignof.

(с C11)

Для того чтобы удовлетворить требованиям выравнивания всех членов структуры, после некоторых её членов может быть вставлено заполнение.

#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));
}

Возможный вывод:

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

Каждый тип объекта накладывает свои требования к выравниванию на каждый объект этого типа. Самое слабое (наименьшее) выравнивание — выравнивание типов char, signed char и unsigned char и равно 1. Самое жёсткое (наибольшее) основное выравнивание любого типа определяется реализацией и равно выравниванию max_align_t(с C11).

Основное выравнивание поддерживается для объектов всех типов сроков хранения.

Если выравнивание объекта делается более жёстким (большим) чем max_align_t с помощью _Alignas, оно имеет расширенное требование выравнивания. Тип структуры или объединения, члены которого имеют расширенное выравнивание, является тип с повышенным выравниванием. Определено ли реализацией поддержка типов с повышенным выравниванием, и может ли их поддержка отличаться для каждого вида срока хранения.

Если тип структуры или объединения S не имеет ни одного члена типа с повышенным выравниванием или объявленного с указателем выравнивания, который определяет расширенное выравнивание, S имеет основное выравнивание.

Атомная версия каждого арифметического или указатель типа имеет основное выравнивание.

(с C11)

Отчёты об ошибках

Следующие отчёты об ошибках, изменяющие поведение, были применены ретроактивно к ранее опубликованным стандартам C.

DR Применено к Поведение как опубликовано Корректное поведение
DR 445 C11 тип может иметь расширенное выравнивание без _Alignas он должен иметь основное выравнивание

Ссылки

  • Стандарт C17 (ISO/IEC 9899:2018):
    • 3.15 объект (стр: 5)
    • 6.2.6 Представления типов (стр: 33-35)
    • 6.2.8 Выравнивание объектов (стр: 36-37)
    • 6.5/6-7 Выражения (стр: 55-56)
  • Стандарт C11 (ISO/IEC 9899:2011):
    • 3.15 объект (стр: 6)
    • 6.2.6 Представления типов (стр: 44-46)
    • 6.2.8 Выравнивание объектов (стр: 48-49)
    • 6.5/6-7 Выражения (стр: 77)
  • Стандарт C99 (ISO/IEC 9899:1999):
    • 3.2 выравнивание (стр: 3)
    • 3.14 объект (стр: 5)
    • 6.2.6 Представления типов (стр: 37-39)
    • 6.5/6-7 Выражения (стр: 67-68)
  • Стандарт C89/C90 (ISO/IEC 9899:1990):
    • 1.6 Определения терминов

См. также

Документация C++ для Объекта

© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/w/c/language/object

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API