Объявление структуры
Структура — это тип, состоящий из последовательности членов, хранение которых выделяется в упорядоченной последовательности (в отличие от объединения, которое представляет собой тип, состоящий из последовательности членов, хранение которых перекрывается).
Спецификатор типа для структуры идентичен спецификатору типа union объединения за исключением используемого ключевого слова:
Синтаксис
struct attr-spec-seq (необязательно) имя (необязательно) { список_объявлений_структуры } | (1) | |
struct attr-spec-seq (необязательно) имя | (2) |
struct имя ;, объявляет, но не определяет структуру name (см. объявление вперёд ниже). В других контекстах, называет ранее объявленную структуру, и attr-spec-seq запрещено.| имя | - | имя структуры, которая определяется |
| список_объявлений_структуры | - | любое количество объявлений переменных, объявлений битовых полей и объявлений static assert. Члены неполного типа и члены типа функции не допускаются (за исключением гибкого массива-члена, описанного ниже) |
| attr-spec-seq | - | (C23)необязательный список атрибутов, применяемых к типу структуры |
Объяснение
Внутри объекта структуры адреса его элементов (и адреса единиц выделения битовых полей) увеличиваются в порядке, в котором члены были определены. Указатель на структуру может быть преобразован в указатель на её первый член (или, если член является битовым полем, в указатель на его единицу выделения). Аналогично, указатель на первый член структуры может быть преобразован в указатель на окружающую структуру. Между любыми двумя членами структуры или после последнего члена может быть неименованная набивка, но не перед первым членом. Размер структуры по крайней мере не меньше суммы размеров её членов.
| Если структура определяет по крайней мере один именованный член, ей разрешается дополнительно объявить свой последний член с неполным типом массива. При доступе к элементу гибкого массива-члена (в выражении, использующем оператор 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) | (с C99) |
| Подобно объединению, безымянный член структуры, тип которой является структурой без имени, известен как безымянная структура. Каждый член безымянной структуры считается членом окружающих структур или объединений, сохраняя их структуру расположения. Это применяется рекурсивно, если окружающая структура или объединение также безымянны. 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Подобно объединению, поведение программы неопределённо, если структура определена без каких-либо именованных членов (включая те, которые получены через безымянные вложенные структуры или объединения). | (с C11) |
Объявление вперёд
Объявление следующего вида
struct attr-spec-seq (необязательно) имя ; |
скрывает любое ранее объявленное значение для имени имя в пространстве тегов и объявляет имя как новое имя структуры в текущем пространстве видимости, которое будет определено позже. До тех пор, пока определение не появится, имя этой структуры имеет неполный тип.
Это позволяет структурам ссылаться друг на друга:
struct y;
struct x { struct y *p; /* ... */ };
struct y { struct x *q; /* ... */ };Обратите внимание, что новое имя структуры также может быть введено просто с помощью тега структуры в другом объявлении, но если в пространстве имён тегов существует ранее объявленная структура с тем же именем, то тег будет ссылаться на это имя
struct s* p = NULL; // tag naming an unknown struct declares it
struct s { int a; }; // definition for the struct pointed to by p
void g(void)
{
struct s; // forward declaration of a new, local struct s
// this hides global struct s until the end of this block
struct s *p; // pointer to local struct s
// without the forward declaration above,
// this would point at the file-scope s
struct s { char* p; }; // definitions of the local struct s
}Ключевые слова
Примечания
См. инициализацию структур для правил, касающихся инициализаторов структур.
Поскольку члены неполного типа недопустимы, а тип структуры неполный до конца определения, структура не может иметь член своего собственного типа. Разрешается указатель на свой тип, и он обычно используется для реализации узлов в связанных списках или деревьях.
Поскольку объявление структуры не устанавливает область видимости, вложенные типы, перечисления и перечислители, введённые объявлениями в список_объявлений_структуры, видны в окружающем пространстве видимости, где определена структура.
Пример
#include <stddef.h>
#include <stdio.h>
int main(void)
{
// Declare the struct type.
struct car
{
char* make;
int year;
};
// Declare and initialize an object of a previously-declared struct type.
struct car c = {.year = 1923, .make = "Nash"};
printf("1) Car: %d %s\n", c.year, c.make);
// Declare a struct type, an object of that type, and a pointer to it.
struct spaceship
{
char* model;
int max_speed;
} ship = {"T-65 X-wing starfighter", 1050},
*pship = &ship;
printf("2) Spaceship: %s. Max speed: %d km/h\n\n", ship.model, ship.max_speed);
// Address increase in order of definition. Padding may be inserted.
struct A { char a; double b; char c; };
printf(
"3) Offset of char a = %zu\n"
"4) Offset of double b = %zu\n"
"5) Offset of char c = %zu\n"
"6) Size of struct 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) Offset of char a = %zu\n"
"8) Offset of char b = %zu\n"
"9) Offset of double c = %zu\n"
"A) Size of struct B = %zu\n\n",
offsetof(struct B, a),
offsetof(struct B, b),
offsetof(struct B, c),
sizeof(struct B)
);
// A pointer to a struct can be cast to a pointer
// to its first member and vice versa.
char** pmodel = (char **)pship;
printf("B) %s\n", *pmodel);
pship = (struct spaceship *)pmodel;
}Возможный вывод:
1) Car: 1923 Nash 2) Spaceship: T-65 X-wing starfighter. Max speed: 1050 km/h 3) Offset of char a = 0 4) Offset of double b = 8 5) Offset of char c = 16 6) Size of struct A = 24 7) Offset of char a = 0 8) Offset of char b = 1 9) Offset of double c = 8 A) Size of struct B = 16 B) T-65 X-wing starfighter
Ссылки
- Стандарт C23 (ISO/IEC 9899:2023):
- 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++ для Объявление класса |
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/w/c/language/struct