Objects and alignment
C++ программы создают, уничтожают, обращаются к объектам и манипулируют ими.
Объект в C — это область хранения данных в среде выполнения, содержимое которой может представлять значения (значение — это смысл содержимого объекта при интерпретации как имеющего конкретный тип ).
Каждый объект имеет
-
размер (можно определить с помощью
sizeof) -
требование выравнивания
(можно определить с помощью
_Alignof(до C23)alignof(начиная с C23) ) (начиная с C11) - класс хранения (автоматический, статический, выделенный, локальный для потока)
- время жизни (равно классу хранения или временное)
- эффективный тип (см. ниже)
- значение (которое может быть неопределённым)
- опционально, идентификатор обозначающий данный объект.
Объекты создаются с помощью объявлений , функций выделения памяти , строковых литералов , составных литералов , а также с помощью не-lvalue выражений, которые возвращают структуры или объединения с элементами массива .
Содержание |
Представление объекта
За исключением
битовых полей
, объекты состоят из непрерывных последовательностей одного или более байтов, каждый из которых состоит из
CHAR_BIT
битов, и могут быть скопированы с помощью
memcpy
в объект типа
unsigned
char
[
n
]
, где
n
— размер объекта. Содержимое результирующего массива известно как
представление объекта
.
Если два объекта имеют одинаковое представление в памяти, они сравниваются как равные (за исключением случаев, когда они являются NaN с плавающей запятой). Обратное неверно: два объекта, которые сравниваются как равные, могут иметь различное представление в памяти, поскольку не каждый бит представления объекта должен участвовать в значении. Такие биты могут использоваться для заполнения для удовлетворения требований выравнивания, для проверки чётности, для указания ловушечных представлений и т.д.
Если представление объекта не представляет никакого значения типа объекта, оно называется trap representation . Доступ к trap representation любым способом, кроме чтения через lvalue-выражение символьного типа, является неопределенным поведением. Значение структуры или объединения никогда не является trap representation, даже если любой конкретный член им является.
Для объектов типа 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 . Смотрите также пример ниже.
Хотя большинство реализаций не допускают trap-представлений, битов заполнения или множественных представлений для целочисленных типов, существуют исключения; например, значение целочисленного типа на Itanium может быть trap-представлением .
Эффективный тип
Каждый объект имеет эффективный тип , который определяет, какие 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* и double* не могут быть алиасами void f1(int* pi, double* pd, double d) { // чтение из *pi может быть выполнено только один раз, до цикла for (int i = 0; i < *pi; i++) *pd++ = d; }
struct S { int a, b; }; // int* и struct S* могут алиасить, так как S является агрегатным типом с членом типа int void f2(int* pi, struct S* ps, struct S s) { // чтение из *pi должно происходить после каждой записи через *ps for (int i = 0; i < *pi; i++) *ps++ = s; }
Обратите внимание, что квалификатор restrict может использоваться для указания, что два указателя не являются алиасами, даже если приведённые выше правила допускают такую возможность.
Обратите внимание, что type-punning также может быть выполнен через неактивный член union .
Выравнивание
Каждый полный тип объекта обладает свойством под названием требование выравнивания , которое представляет собой целочисленное значение типа size_t , обозначающее количество байтов между последовательными адресами, по которым могут размещаться объекты данного типа. Допустимые значения выравнивания являются неотрицательными целыми степенями двойки.
|
Требование выравнивания типа может быть запрошено с помощью
|
(начиная с C11) |
Для удовлетворения требований выравнивания всех членов структуры, после некоторых её членов может быть вставлено заполнение.
#include <stdalign.h> #include <stdio.h> // объекты структуры S могут быть размещены по любому адресу // так как и S.a, и S.b могут быть размещены по любому адресу struct S { char a; // размер: 1, выравнивание: 1 char b; // размер: 1, выравнивание: 1 }; // размер: 2, выравнивание: 1 // объекты структуры X должны быть размещены по 4-байтным границам // потому что X.n должен быть размещен по 4-байтным границам // так как требование выравнивания для int (обычно) равно 4 struct X { int n; // размер: 4, выравнивание: 4 char c; // размер: 1, выравнивание: 1 // три байта заполнения }; // размер: 8, выравнивание: 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 (since C11) .
Фундаментальные выравнивания поддерживаются для объектов всех видов продолжительностей хранения.
|
Если выравнивание объекта сделано более строгим (большим), чем
max_align_t
с использованием
Если тип структуры или объединения
Атомарная версия каждого арифметического или указательного типа имеет фундаментальное выравнивание. |
(начиная с C11) |
Отчеты о дефектах
Следующие отчеты об изменениях поведения, влияющие на дефекты, были применены задним числом к ранее опубликованным стандартам C.
| DR | Applied to | Behavior as published | Correct behavior |
|---|---|---|---|
| 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 object (стр. 6)
-
- 6.2.6 Representations of types (стр. 44-46)
-
- 6.2.8 Alignment of objects (стр. 48-49)
-
- 6.5/6-7 Expressions (стр. 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++ documentation
для
Object
|