Namespaces
Variants

Struct declaration

From cppreference.net

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

Спецификатор типа для структуры идентичен спецификатору типа union за исключением используемого ключевого слова:

Содержание

Синтаксис

struct attr-spec-seq  (необязательно) name  (необязательно) { struct-declaration-list } (1)
struct attr-spec-seq  (необязательно) name (2)
1) Определение структуры: вводит новый тип struct name и определяет его значение
2) Если используется на отдельной строке, как в struct name ; , объявляет , но не определяет структуру name (см. forward declaration ниже). В других контекстах именует ранее объявленную структуру, и attr-spec-seq не допускается.
name - имя структуры, которая определяется
struct-declaration-list - любое количество объявлений переменных, bit-field объявлений и static assert объявлений. Члены неполного типа и члены функционального типа не допускаются (за исключением гибкого элемента массива, описанного ниже)
attr-spec-seq - (C23) опциональный список attributes , применяемых к типу структуры

Объяснение

Внутри объекта структуры адреса её элементов (и адреса единиц распределения битовых полей) увеличиваются в порядке, в котором члены были определены. Указатель на структуру может быть приведён к указателю на её первый член (или, если член является битовым полем, к его единице распределения). Аналогично, указатель на первый член структуры может быть приведён к указателю на включающую структуру. Между любыми двумя членами структуры или после последнего члена может быть неназванное заполнение, но не перед первым членом. Размер структуры по крайней мере не меньше суммы размеров её членов.

Если структура определяет хотя бы один именованный член, ей разрешено дополнительно объявлять свой последний член с неполным типом массива. Когда осуществляется доступ к элементу гибкого массива (в выражении, использующем оператор . или -> с именем гибкого массива в качестве правого операнда), то структура ведет себя так, как если бы массив имел наибольший размер, помещающийся в память, выделенную для этого объекта. Если дополнительная память не была выделена, она ведет себя как массив с 1 элементом, за исключением того, что поведение не определено, если осуществляется доступ к этому элементу или создается указатель на следующий за ним элемент. Инициализация и оператор присваивания игнорируют гибкий массив. sizeof опускает его, но может иметь больше завершающего заполнения, чем подразумевает это опущение. Структуры с гибкими массивами (или объединения, имеющие рекурсивно-возможный член структуры с гибким массивом) не могут появляться как элементы массивов или как члены других структур.

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

Примечания

См. инициализацию структур для правил, касающихся инициализаторов структур.

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

Поскольку объявление структуры не создаёт область видимости , вложенные типы, перечисления и перечислители, введённые объявлениями внутри 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++ для Объявление класса