Struct mystruct
{
int i;
char str[21];
double d;
} s, *sptr=&s;
...
s.i = 3; // присвоение члену i структуры mystruct s;
sptr->d = 1.23; // присвоение компоненту d структуры mystruct s;
Если структура B содержит поле, тип которого есть структура A, то доступ к компонентам A выполняется через два одновременно задаваемых выбора компонента структуры. Например:
Struct A
{
int j;
double x;
};
Struct B
{
int i;
struct A a;
double d;
} s, *sptr;
...
s.i = 3; // присвоение компоненту i структуры B
s.a.j = 2; // присвоение компоненту j структуры A
sptr->d = 1.23; // присвоение компоненту d структуры B
(sptr->).x = 3.14 // присвоение компоненту x структуры A
Каждое объявление структуры вводит уникальный тип, поэтому в структуре:
Struct A
{
int i,j;
double d;
} a, a1;
Struct B
{
int i,j;
double d;
} b;
объекты a и a1 оба имеют тип struct A, но объекты a и b имеют различные типы структуры.
Структурам могут присваиваться значения только в том случае, если и исходная структура, и структура назначения имеют один и тот же тип. Например:
a = a1; // так можно; тип один и тот же, поэтому может быть
// выполнено покомпонентное присвоение структур;
a = b; // так нельзя; различные типы;
a.1 = b.1; a.j = b.j; a.d = b.d; // такое присваивание на
// уровне компонентов структуры можно выполнять;
Память распределяется в структуре покомпонентно, слева направо, от младшего к старшему адресу памяти. В следующем примере:
struct mystruct {
int i;
char str[21];
double d;
} s;
объект s занимает достаточное количество памяти для размещения целочисленного значения типа int, 21-байтовой строки и 8-байтового значения типа double.
Имена тегов структур разделяют общее пространство имен с тегами объединений и перечислений (однако в языке С++ имена входящих в структуру перечислений находятся в другом адресном пространстве). Это означает, что в пределах одного контекста такие теги должны иметь уникальные имена. Тем не менее, имена тегов не обязаны отличаться от идентификаторов, находящихся в трех других адресных пространствах: пространстве имен меток, пространстве имен компонентов и едином адресном пространстве.
Имена компонентов в пределах данной структуры или объединения обязаны быть уникальными, но среди разных структур или объединений они могут совпадать. Например:
goto s;
...
s: struct s { // так можно; теги и имена меток находятся в разных адресных пространствах;
int s // так можно; теги, имена меток и имена компонентов находятся в разных адресных пространствах;
float s; // так нельзя: повторение имени компонентов структур;
} s; /* так можно; пространства имен переменных различны. В языке С++ это допустимо только если s не имеет конструктора */
union s { // так нельзя: повторение имен в пространстве тегов;
int s; // так можно: новое пространство компонентов;
float f;
} f; // так можно: пространство имен переменных;
struct t {
int s; // так можно: следующее пространство имен компонентов;
...
} s; // так нельзя: повторение имен переменных
Указатель на структуру типа А допустим в объявлении другой структуры В до объявления структуры А. Например:
struct A; // неполное объявление;
struct B { struct A *pa };
struct A { struct B *pb };
Первое объявление А называется неполным, поскольку в этой точке отсутствует определение А. В данной ситуации неполное объявление допустимо, поскольку в объявлении В размер А необязателен.
Структура может содержать любые комбинации битовых полей с данными других типов.
Целочисленные компоненты типа signed или unsigned можно объявить битовыми полями шириной от 1 до 16 бит. Ширина битового поля и его опциональный идентификатор задаются следующим образом:
Спецификатор_типа <идентификатор-битового поля>:ширина;
где спецификатор_типа это char, unsigned char, int или unsigned int. Битовые поля располагаются, начиная с младшего и кончая старшим битом слова. Выражение "ширина" должно быть задано и должно давать целочисленную константу со значением в диапазоне от 1 до 16.
Если идентификатор битового поля опущен, то число битов, заданное выражением "ширина", распределяется в памяти, но поле при этом остается недоступным программе. Это позволяет создавать битовые шаблоны для, например, аппаратных регистров компьютера, в которых некоторые биты не используются. Например, структура
struct mystruct {
int i:2;
unsigned j:5;
int :4;
int k:1;
unsigned m:4;
} a, b, c;
создает следующее распределение памяти:
Для битового поля типа int (например, signed) старший бит интерпретируется как знаковый бит. Битовое поле шириной 2, содержащее двоичное 11, будет, следовательно, в случае типа unsigned интерпретироваться как 3, а в случае int как -1. В данном примере выражение a.i = 6 поместит в a.i двоичное 10 = -0, не выдавая каких-либо предупреждений. Поле k типа signed int шириной 1 может содержать только значения 1 и 0, так как битовый шаблон 1 будет интерпретирован как «минус» (-).
Примечание: Битовые поля могут быть объявлены только в структурах, объединениях и классах. Доступ к ним выполняется тем же способом выбоpа компонентов (.) и (->), что и для небитовых компонентов. Битовые поля вызывают некоторые проблемы, когда записывается переносимый код, поскольку организация битов в байтах и байтов в словах зависит от конкретной машины. Выражение &mystruct.x недопустимо, так как x – это идентификатор битового поля, и никакой гарантии, что mystruct.x имеет адрес на границе байта, нет.