Spec-Zone.ru › C

Объявление структуры

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

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

Синтаксис

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

Пояснение

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

Если структура определяет хотя бы один именованный член, её последний член может дополнительно иметь неполный тип массива. При обращении к элементу члена гибкого массива (в выражении, использующем оператор . или ->, где имя члена гибкого массива является правым операндом), структура ведёт себя так, как если бы член-массив имел наибольший размер, помещающийся в памяти, выделенной для этого объекта. Если дополнительная память не выделялась, поведение соответствует массиву из одного элемента, за исключением того, что поведение не определено, если к этому элементу выполняется обращение или формируется указатель на позицию сразу после него. Инициализация и оператор присваивания игнорируют член гибкого массива. 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)
(начиная с C99)

Аналогично объединению, безымянный член структуры, тип которого является структурой без name, называется анонимной структурой. Каждый член анонимной структуры считается членом охватывающей её структуры или объединения, при этом сохраняется расположение членов в памяти. Это правило применяется рекурсивно, если охватывающая структура или объединение также являются анонимными.

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 (необязательно) name ;

скрывает любое ранее объявленное значение имени name в пространстве имён тегов и объявляет name новым именем структуры в текущей области видимости, определение которой появится позднее. До появления определения это имя структуры имеет неполный тип.

Это позволяет структурам ссылаться друг на друга:

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
}

Ключевые слова

struct

Примечания

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

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

Поскольку объявление структуры не создаёт область видимости, вложенные типы, перечисления и элементы перечислений, введённые объявлениями в struct-declaration-list, видны в окружающей области видимости, где определяется структура.

Пример

#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

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

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

DR Применён к Поведение согласно опубликованному тексту Корректное поведение
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++ для Объявление класса

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

Spec-Zone.ru

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