Structures and unions
A structure contains an ordered group of data objects. Unlike the elements of an array, the data objects within a structure can have varied data types. Each data object in a structure is a member or field.
A union is an object similar to a structure except that all of its members start at the same location in memory. A union variable can represent the value of only one of its members at a time.
In C++, structures and unions are the same as classes except that their members and inheritance are public by default.
You can declare a structure or union type separately from the definition of variables of that type, as described in Structure and union type definition and Structure and union variable declarations; or you can define a structure or union data type and all variables that have that type in one statement, as described in Structure and union type and variable definitions in a single statement.
Structures and unions are subject to alignment considerations. For information about changing alignment and packing structures, see The _Packed qualifier (C only) and #pragma pack.
Structure and union type definition
A
structure or union type definition contains the struct
or union
keyword
followed by an optional identifier (the structure tag) and a brace-enclosed
list of members.
Structure or union type definition syntax
The tag_identifier gives a name to the
type. If you do not provide a tag name, you must put all variable
definitions that refer to the type within the declaration of the type,
as described in Structure and union type and variable definitions in a single statement. Similarly, you
cannot use a type qualifier with a structure or union definition; type
qualifiers placed in front of the struct
or union
keyword
can only apply to variables that are declared within the type definition.
Member declarations
The list of members provides a structure or union data type with a description of the values that can be stored in the structure or union. The definition of a member has the form of a standard variable declaration. The names of member variables must be distinct within a single structure or union, but the same member name may be used in another structure or union type that is defined within the same scope, and may even be the same as a variable, function, or type name.
- any variably modified type
void
type- a function
- any incomplete type
A
union member cannot be a class object that has a constructor, destructor,
or overloaded copy assignment operator, nor can it be of reference
type. A union member cannot be declared with the keyword static
.
A member that does not represent a bit field
can be qualified with either of the type qualifiers volatile
or const
.
The result is an lvalue.
Structure members are assigned to memory addresses in increasing order, with the first component starting at the beginning address of the structure name itself. To allow proper alignment of components, padding bytes may appear between any consecutive members in the structure layout.
The storage allocated for a union is the storage required for the largest member of the union (plus any padding that is required so that the union will end at a natural boundary of its member having the most stringent requirements). All of a union's components are effectively overlaid in memory: each member of a union is allocated storage starting at the beginning of the union, and only one member can occupy the storage at a time.
- Flexible array members
A flexible array member is an unbounded array that occurs within a structure. It is a C99 feature and the z/OS XL C/C++ compiler supports it as an IBM extension. Flexible array members can be used to access a variable-length object. A flexible array member is permitted as the last member of a structure, provided that the structure has more than one named member. It is declared with an empty index as follows:
array_identifier [ ];
b
is a flexible
array member of structure f
. struct f{
int a;
int b[];
};
Because a flexible array member has an
incomplete type, you cannot apply the sizeof
operator
to a flexible array. In this example, the statement sizeof(f)
returns
the same result as sizeof(f.a)
, which is the size
of an integer. The statement sizeof(f.b)
cannot be
used, because b
is a flexible array member that has
an incomplete type.struct f{
int a;
int b[];
};
struct f fa[10]; // Error.
- Flexible array members can be declared in any part
of a structure, not just as the last member. The type of any member
that follows the flexible array member is not required to be compatible
with the type of the flexible array member; however, a warning message
is issued when a flexible array member is followed by members of an
incompatible type. The following example demonstrates this:
struct s { int a; int b[]; char c; // The compiler issues a warning message. } f;
- Structures containing flexible array members can be members of other structures.
- Flexible array members can be statically
initialized only if either of the following two conditions is true:
- The flexible array member is the last member of the structure,
for example:
struct f { int a; int b[]; } f1 = {1,{1,2,3}}; // Fine. struct a { int b; int c[]; int d[]; } e = { 1,{1,2},3}; // Error, c is not the last member // of structure a.
- Flexible array members are contained in the outermost structure
of nested structures. Members of inner structures cannot be statically
initialized, for example:
struct b { int c; int d[]; }; struct c { struct b f; int g[]; } h ={{1,{1,2}},{1,2}}; // Error, member d of structure b is // in the inner nested structure.
- The flexible array member is the last member of the structure,
for example:
- Zero-extent array members (IBM extension)
Zero-extent arrays are provided for GNU C/C++ compatibility, and can be used to access a variable-length object.
array_identifier [0]
For
example, b
is a zero-extent array member of structure f
. struct f{
int a;
int b[0];
};
The sizeof
operator
can be applied to a zero-extent array, and the value returned is 0.
In this example, the statement sizeof(f)
returns
the same result as sizeof(f.a)
, which is the size
of an integer. The statement sizeof(f.b)
returns
0.struct f{
int a;
int b[0];
};
struct f fa[10]; // Fine.
{}
.
Otherwise, it must be initialized as a dynamically allocated array.
For example: struct f{
int a;
int b[0];
};
struct f f1 = {100, {}}; //Fine.
struct f f2 = {100, {1, 2}}; //Error.
#include <stdio.h>
struct s {
int a;
int b[0];
};
struct t1 {
struct s f;
int c[3];
} g1 = {{1},{1,2}};
struct t2 {
struct s f;
int c[3];
} g2 = {{1,{}},{1,2}};
int main() {
printf("%d %d %d %d\n", g1.f.a, g1.f.b[0], g1.f.b[1], g1.f.b[2]);
printf("%d %d %d %d\n", g2.f.a, g2.f.b[0], g2.f.b[1], g2.f.b[2]);
return 0;
}
In this example, the two printf
statements
produce the same output:1 1 2 0
struct s {
int a;
int b[0];
char c; // Issues a warning message
} f;
int func(){
int a[0]; // error
struct S{
int x;
char b[0]; // fine
};
}
- Bit field members
Both C and C++ allow integer members to be stored into memory spaces smaller than the compiler would ordinarily allow. These space-saving structure members are called bit fields, and their width in bits can be explicitly declared. Bit fields are used in programs that must force a data structure to correspond to a fixed hardware representation and are unlikely to be portable.
Bit field member declaration syntax
The constant_expression is a constant
integer expression that indicates the field width in bits. A bit field
declaration may not use either of the type qualifiers const
or volatile
.
_Bool
, int
, signed
int
, and unsigned int
.A bit field can be any integral type or enumeration type.
kingdom, phylum
,
and genus
, occupying 12, 6, and 2 bits respectively:
struct taxonomy {
int kingdom : 12;
int phylum : 6;
int genus : 2;
};
When you assign a value that is out of range to a bit field, the low-order bit pattern is preserved and the appropriate bits are assigned.
- Define an array of bit fields
- Take the address of a bit field
- Have a pointer to a bit field
- Have a reference to a bit field
struct {
int larry : 25; // Bit Field: offset 0 bytes and 0 bits.
int curly : 25; // Bit Field: offset 3 bytes and 1 bit (25 bits).
int moe; // non-Bit Field: offset 8 bytes and 0 bits (64 bits).
} stooges;
There is no padding between larry and curly.
The bit offset of curly would be 25 bits. The member moe would
be aligned on the next 4 byte boundary, causing 14 bits a padding
between curly and moe.Bit fields with a length of 0 must be unnamed. Unnamed bit fields cannot be referenced or initialized.
A zero-width bit field causes the next field
to be aligned on the next container boundary. However, a _Packed
(C
only) structure, which has a zero-width bit field, causes the next
field to be aligned on the next byte boundary.
int
occupies 4 bytes. The example
declares the identifier kitchen
to be of type struct
on_off
: struct on_off {
unsigned light : 1;
unsigned toaster : 1;
int count; /* 4 bytes */
unsigned ac : 4;
unsigned : 4;
unsigned clock : 1;
unsigned : 0;
unsigned flag : 1;
} kitchen;
kitchen
contains
eight members totalling 16 bytes. The following table describes the
storage that each member occupies:
Member name | Storage occupied |
---|---|
light |
1 bit |
toaster |
1 bit |
(padding — 30 bits) | To the next int boundary |
count |
The size of an int (4
bytes) |
ac |
4 bits |
(unnamed field) | 4 bits |
clock |
1 bit |
(padding — 23 bits) | To the next int boundary
(unnamed field) |
flag |
1 bit |
(padding — 31 bits) | To the next int boundary |
Structure and union variable declarations
A structure or union declaration has the same form as a definition except the declaration does not have a brace-enclosed list of members. You must declare the structure or union data type before you can define a variable having that type.
Structure or union variable declaration syntax
The tag_identifier indicates the data type of the structure or union.
The keyword struct
is optional in structure
variable declarations.
You can declare
structures or unions having any storage class. The storage class specifier
and any type qualifiers for the variable must appear at the beginning
of the statement. Structures or unions declared with the register
storage
class specifier are treated as automatic variables.
address
: struct address {
int street_no;
char *street_name;
char *city;
char *prov;
char *postal_code;
};
address
: struct address perm_address;
struct address temp_address;
Structure and union type and variable definitions in a single statement
length
): union {
float meters;
double centimeters;
long inches;
} length;
Note that because this example does not
name the data type, length
is the only variable that
can have this data type. Putting an identifier after struct
or union
keyword
provides a name for the data type and lets you declare additional
variables of this data type later in the program.
static struct {
int street_no;
char *street_name;
char *city;
char *prov;
char *postal_code;
} perm_address, temp_address;
In this case, both perm_address
and temp_address
are
assigned static storage.volatile struct class1 {
char descript[20];
long code;
short complete;
} file1, file2;
struct class1 {
char descript[20];
long code;
short complete;
} volatile file1, file2;
In both cases, the structures file1
and file2
are
qualified as volatile
.
Access to structure and union members
.
)
or a pointer with the arrow operator (->
) and
the member name. For example, both of the following: perm_address.prov = "Ontario";
p_perm_address -> prov = "Ontario";
assign the string "Ontario"
to
the pointer prov
that is in the structure perm_address
.All
references to members of structures and unions, including bit fields,
must be fully qualified. In the previous example, the fourth field
cannot be referenced by prov
alone, but only by perm_address.prov
.
Anonymous structures (C11)
- The structure is nested inside another structure or union.
- The structure has no tag.
- The structure has no name.
struct v {
union {
// This is an anonymous structure, because it has no tag, no name,
// and is a member of another structure or union.
struct { int i, j; };
// This is not an anonymous structure, because it has a name.
struct { long k, l; } w;
// This is not an anonymous structure, because
// the structure has a tag "phone".
struct phone {int number, areanumber;};
};
int m;
} v1;
Anonymous unions
An anonymous union is a union that does not have a tag or a name and that is a member of another union or structure. It cannot be followed by a declarator. An anonymous union is not a type; it defines an unnamed object.
z/OS XL C supports anonymous unions only under extended language levels.
The member names of an anonymous union must be distinct from other names within the scope in which the union is declared. You can use member names directly in the union scope without any additional member access syntax.
i
and cptr
directly
because they are in the scope containing the anonymous union. Because i
and cptr
are
union members and have the same address, you should only use one of
them at a time. The assignment to the member cptr
will
change the value of the member i
. void f() {
union { int i; char* cptr ; };
/* . . . */
i = 5;
cptr = "string_in_union"; // Overrides the value 5.
}
An anonymous
union cannot have protected or private members, and it cannot have
member functions. A global or namespace anonymous union must be declared
with the keyword static
.