Struct declaration
Структура — это тип, состоящий из последовательности членов, чьё хранилище распределено в упорядоченной последовательности (в отличие от объединения, которое является типом, состоящим из последовательности членов, чьё хранилище перекрывается).
Спецификатор типа
для структуры идентичен спецификатору типа
union
за исключением используемого ключевого слова:
Содержание |
Синтаксис
struct
attr-spec-seq
(необязательно)
name
(необязательно)
{
struct-declaration-list
}
|
(1) | ||||||||
struct
attr-spec-seq
(необязательно)
name
|
(2) | ||||||||
struct
name
;
,
объявляет
, но не определяет структуру
name
(см. forward declaration ниже). В других контекстах именует ранее объявленную структуру, и
attr-spec-seq
не допускается.
| name | - | имя структуры, которая определяется |
| struct-declaration-list | - | любое количество объявлений переменных, bit-field объявлений и static assert объявлений. Члены неполного типа и члены функционального типа не допускаются (за исключением гибкого элемента массива, описанного ниже) |
| attr-spec-seq | - | (C23) опциональный список attributes , применяемых к типу структуры |
Объяснение
Внутри объекта структуры адреса её элементов (и адреса единиц распределения битовых полей) увеличиваются в порядке, в котором члены были определены. Указатель на структуру может быть приведён к указателю на её первый член (или, если член является битовым полем, к его единице распределения). Аналогично, указатель на первый член структуры может быть приведён к указателю на включающую структуру. Между любыми двумя членами структуры или после последнего члена может быть неназванное заполнение, но не перед первым членом. Размер структуры по крайней мере не меньше суммы размеров её членов.
|
Если структура определяет хотя бы один именованный член, ей разрешено дополнительно объявлять свой последний член с неполным типом массива. Когда осуществляется доступ к элементу гибкого массива (в выражении, использующем оператор
struct s { int n; double d[]; }; // s.d is a flexible array member struct s t1 = { 0 }; // OK, d is as if double d[1], but UB to access struct s t2 = { 1, { 4.2 } }; // error: initialization ignores flexible array // if sizeof (double) == 8 struct s *s1 = malloc(sizeof (struct s) + 64); // as if d was double d[8] struct s *s2 = malloc(sizeof (struct s) + 40); // as if d was double d[5] s1 = malloc(sizeof (struct s) + 10); // now as if d was double d[1]. Two bytes excess. double *dp = &(s1->d[0]); // OK *dp = 42; // OK s1->d[1]++; // Undefined behavior. 2 excess bytes can't be accessed // as double. s2 = malloc(sizeof (struct s) + 6); // same, but UB to access because 2 bytes are // missing to complete 1 double dp = &(s2->d[0]); // OK, can take address just fine *dp = 42; // undefined behavior *s1 = *s2; // only copies s.n, not any element of s.d // except those caught in sizeof (struct s) |
(since C99) |
|
Аналогично union, неименованный член структуры, тип которого является структурой без имени , называется анонимной структурой . Каждый член анонимной структуры считается членом охватывающей структуры или union, сохраняя их структурную компоновку. Это применяется рекурсивно, если охватывающая структура или union также является анонимной. struct v { union // anonymous union { struct { int i, j; }; // anonymous structure struct { long k, l; } w; }; int m; } v1; v1.i = 2; // valid v1.k = 3; // invalid: inner structure is not anonymous v1.w.k = 5; // valid Аналогично union, поведение программы не определено, если структура определена без каких-либо именованных членов (включая полученные через анонимные вложенные структуры или union). |
(since C11) |
Предварительное объявление
Объявление следующего вида
struct
attr-spec-seq
(optional)
name
;
|
|||||||||
скрывает любое ранее объявленное значение имени name в пространстве имен тегов и объявляет name как новое имя структуры в текущей области видимости, которое будет определено позднее. До появления определения это имя структуры имеет неполный тип .
Это позволяет структурам ссылаться друг на друга:
struct y; struct x { struct y *p; /* ... */ }; struct y { struct x *q; /* ... */ };
Обратите внимание, что новое имя структуры также может быть введено просто с использованием тега структуры в другом объявлении, но если ранее объявленная структура с таким же именем существует в пространстве имён тегов, то тег будет ссылаться на это имя
struct s* p = NULL; // объявление тега неизвестной структуры объявляет её struct s { int a; }; // определение структуры, на которую указывает p void g(void) { struct s; // предварительное объявление новой локальной структуры s // это скрывает глобальную структуру s до конца этого блока struct s *p; // указатель на локальную структуру s // без предварительного объявления выше // это указывало бы на структуру s в области видимости файла struct s { char* p; }; // определение локальной структуры s }
Ключевые слова
Примечания
См. инициализацию структур для правил, касающихся инициализаторов структур.
Поскольку члены неполного типа не допускаются, а тип структуры не является полным до конца определения, структура не может иметь член собственного типа. Указатель на собственный тип допускается и обычно используется для реализации узлов в связных списках или деревьях.
Поскольку объявление структуры не создаёт область видимости , вложенные типы, перечисления и перечислители, введённые объявлениями внутри struct-declaration-list , видны в окружающей области видимости, где определена структура.
Пример
#include <stddef.h> #include <stdio.h> int main(void) { // Объявление типа структуры. struct car { char* make; int year; }; // Объявление и инициализация объекта ранее объявленного типа структуры. struct car c = {.year = 1923, .make = "Nash"}; printf("1) Автомобиль: %d %s\n", c.year, c.make); // Объявление типа структуры, объекта этого типа и указателя на него. struct spaceship { char* model; int max_speed; } ship = {"T-65 X-wing starfighter", 1050}, *pship = &ship; printf("2) Космический корабль: %s. Макс. скорость: %d км/ч\n\n", ship.model, ship.max_speed); // Адреса увеличиваются в порядке определения. Может быть вставлено выравнивание. struct A { char a; double b; char c; }; printf( "3) Смещение char a = %zu\n" "4) Смещение double b = %zu\n" "5) Смещение char c = %zu\n" "6) Размер структуры A = %zu\n\n", offsetof(struct A, a), offsetof(struct A, b), offsetof(struct A, c), sizeof(struct A) ); struct B { char a; char b; double c; }; printf( "7) Смещение char a = %zu\n" "8) Смещение char b = %zu\n" "9) Смещение double c = %zu\n" "A) Размер структуры B = %zu\n\n", offsetof(struct B, a), offsetof(struct B, b), offsetof(struct B, c), sizeof(struct B) ); // Указатель на структуру может быть приведен к указателю // на ее первый член и наоборот. char** pmodel = (char **)pship; printf("B) %s\n", *pmodel); pship = (struct spaceship *)pmodel; }
Возможный вывод:
1) Автомобиль: 1923 Nash 2) Космический корабль: T-65 X-wing starfighter. Макс. скорость: 1050 км/ч 3) Смещение char a = 0 4) Смещение double b = 8 5) Смещение char c = 16 6) Размер структуры A = 24 7) Смещение char a = 0 8) Смещение char b = 1 9) Смещение double c = 8 A) Размер структуры B = 16 B) T-65 X-wing starfighter
Отчеты о дефектах
Следующие отчеты об изменениях поведения, влияющие на дефекты, были применены задним числом к ранее опубликованным стандартам C.
| DR | Applied to | Behavior as published | Correct behavior |
|---|---|---|---|
| DR 499 | C11 | члены анонимных структур/объединений считались членами охватывающей структуры/объединения | они сохраняют свою структуру памяти |
Ссылки
- Стандарт C23 (ISO/IEC 9899:2024):
-
- 6.7.2.1 Спецификаторы структур и объединений (стр.: TBD)
- Стандарт C17 (ISO/IEC 9899:2018):
-
- 6.7.2.1 Спецификаторы структур и объединений (стр: 81-84)
- Стандарт C11 (ISO/IEC 9899:2011):
-
- 6.7.2.1 Спецификаторы структур и объединений (стр: 112-117)
- Стандарт C99 (ISO/IEC 9899:1999):
-
- 6.7.2.1 Спецификаторы структур и объединений (стр: 101-104)
- Стандарт C89/C90 (ISO/IEC 9899:1990):
-
- 3.5.2.1 Спецификаторы структур и объединений
Смотрите также
|
Документация C++
для
Объявление класса
|