C++ formally defines the concept of a compound type and how one can
be constructed. Many of the compound types originated in C.
You are using a compound type when you construct any of the following:
Related References
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.
In C++, a structure member must be a complete type.
Use structures to group logically related objects. For example, to allocate storage for the components of one address, define the following variables:
int street_no; char *street_name; char *city; char *prov; char *postal_code;
To allocate storage for more than one address, group the components of each address by defining a structure data type and as many variables as you need to have the structure data type.
In C++, a structure is the same as a class except that its members
and inheritance are public by default.
In the following example, line int street_no; through to char *postal_code; declare the structure tag address:
struct address { int street_no; char *street_name; char *city; char *prov; char *postal_code; }; struct address perm_address; struct address temp_address; struct address *p_perm_address = &perm_address;
The variables perm_address and temp_address are instances of the structure data type address. Both contain the members described in the declaration of address. The pointer p_perm_address points to a structure of address and is initialized to point to perm_address.
Refer to a member of a structure by specifying the structure variable name with the dot operator (.) 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 a pointer to the string "Ontario" to the pointer prov that is in the structure perm_address.
All references to structures must be fully qualified. In the example, you cannot reference the fourth field by prov alone. You must reference this field by perm_address.prov.
Structures with identical members but different names are not compatible and cannot be assigned to each other.
Structures are not intended to conserve storage. If you need direct control of byte mapping, use pointers.
Each structure definition creates a new structure type that is neither the
same as nor compatible with any other structure type in the same source
file. However, a type specifier that is a reference to a previously
defined structure type is the same type. The structure tag associates
the reference with the definition, and effectively acts as the type
name. To illustrate this, only the types of structures j and
k are the same.
struct { int a; int b; } h; struct { int a; int b; } i; struct S { int a; int b; } j; struct S k;
Related References
A structure type definition describes the members that are part of the structure. It contains the struct keyword followed by an optional identifier (the structure tag) and a brace-enclosed list of members.
A declaration of a structure data type has the form:
>>-struct--+-identifier--------------------------+------------->< | .-----------. | | V | | '-+------------+--{----member--;-+--}-' '-identifier-'
The keyword struct followed by an identifier (tag) gives a name to the data type. If you do not provide a tag name, you must put all variable definitions that refer to it within the declaration of the data type.
A structure declaration has the same form as a structure definition except the declaration does not have a brace-enclosed list of members. A structure definition has the same form as the declaration of that structure data type, but ends with a semicolon.
Defining Structure Members
The list of members provides the structure data type with a description of the values that can be stored in the structure. In C, a structure member may be of any type except "function returning T" (for some type T), any incomplete type, and void. Because incomplete types are not allowed as a structure member, a structure type may not contain an instance of itself as a member, but is allowed to contain a pointer to an instance of itself.
The definition of a structure member has the form of a variable declaration. The names of structure members must be distinct within a single structure, but the same member name may be used in another structure type that is defined within the same scope, and may even be the same as a variable, function, or type name. A member that does not represent a bit field can be of any data type, which can be qualified with either of the type qualifiers volatile or const. The result is an lvalue. However, a bit field without a type qualifier can be declared as a structure member. If the bit field is unnamed, it does not participate in initialization, and will have indeterminate value after initialization.
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, holes or padding may
appear between any consecutive members in the structure layout.
Related References
A structure variable definition contains an optional storage class keyword,
the struct keyword, a structure tag, a declarator, and an optional
identifier. The structure tag indicates the data type of the structure
variable.
The keyword struct is optional in C++.
You can declare structures having any storage class. Structures declared with the register storage class specifier are treated as automatic structures.
Related References
An initializer for a structure is a brace-enclosed comma-separated list of values. An initializer is preceded by an equal sign (=). In the absence of designations, memory for structure members is allocated in the order declared, and memory address are assigned in increasing order, with the first component starting at the beginning address of the structure name itself. You do not have to initialize all members of a structure. The default initializer for a structure with static storage is the recursive default for each component; a structure with automatic storage has none.
The initializer for an automatic variable of a structure or any
aggregate type must be a constant expression.
Example
The following definition shows a completely initialized structure:
struct address { int street_no; char *street_name; char *city; char *prov; char *postal_code; }; static struct address perm_address = { 3, "Savona Dr.", "Dundas", "Ontario", "L4B 2A1"};
The values of perm_address are:
The following definition shows a partially initialized structure:
struct address { int street_no; char *street_name; char *city; char *prov; char *postal_code; }; struct address temp_address = { 44, "Knyvet Ave.", "Hamilton", "Ontario" };
The values of temp_address are:
To define a structure type and a structure variable in one statement, put a
declarator and an optional initializer after the type definition. To
specify a storage class specifier for the variable, you must put the storage
class specifier at the beginning of the statement.
For example:
static struct { int street_no; char *street_name; char *city; char *prov; char *postal_code; } perm_address, temp_address;
Because this example does not name the structure data type, perm_address and temp_address are the only structure variables that will have this data type. Putting an identifier after struct, lets you make additional variable definitions of this data type later in the program.
The structure type (or tag) cannot have the volatile qualifier, but a member or a structure variable can be defined as having the volatile qualifier.
For example:
static struct class1 { char descript[20]; volatile long code; short complete; } volatile file1, file2; struct class1 subfile;
This example qualifies the structures file1 and file2, and the structure member subfile.code as volatile.
Related References
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.
The syntax for declaring a bit field is as follows:
>>-type_specifier--+------------+--:--constant_expression--;--->< '-declarator-'
A bit field declaration contains a type specifier followed by an optional declarator, a colon, a constant integer expression that indicates the field width in bits, and a semicolon. A bit field declaration may not use either of the type qualifiers, const or volatile.
C++ extends the list of allowable types for bit fields to include
any integral type or enumeration type.
In either language, 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.
Bit fields with a length of 0 must be unnamed. Unnamed bit fields cannot be referenced or initialized. A zero-width bit field can cause the next field to be aligned on the next container boundary where the container is the same size as the underlying type of the bit field.
The following restrictions apply to bit fields. You cannot:
The following structure has three bit-field members kingdom, phylum, and genus, occupying 12, 6, and 2 bits respectively:
struct taxonomy { int kingdom : 12; int phylum : 6; int genus : 2; };
Alignment of Bit Fields
If a series of bit fields does not add up to the size of an int, padding can take place. The amount of padding is determined by the alignment characteristics of the members of the structure.
The following example demonstrates padding, and is valid for all implementations. Suppose that an 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 ;
The structure 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) | 1 bit |
clock | 1 bit |
(padding -- 23 bits) | To the next int boundary (unnamed field) |
flag | 1 bit |
(padding -- 31 bits) | To the next int boundary |
All references to structure fields must be fully qualified. For instance, you cannot reference the second field by toaster. You must reference this field by kitchen.toaster.
The following expression sets the light field to 1:
kitchen.light = 1;
When you assign to a bit field a value that is out of its range, the bit pattern is preserved and the appropriate bits are assigned. The following expression sets the toaster field of the kitchen structure to 0 because only the least significant bit is assigned to the toaster field:
kitchen.toaster = 2;
Related References
The following program finds the sum of the integer numbers in a linked list:
/** ** Example program illustrating structures using linked lists **/ #include <stdio.h> struct record { int number; struct record *next_num; }; int main(void) { struct record name1, name2, name3; struct record *recd_pointer = &name1; int sum = 0; name1.number = 144; name2.number = 203; name3.number = 488; name1.next_num = &name2; name2.next_num = &name3; name3.next_num = NULL; while (recd_pointer != NULL) { sum += recd_pointer->number; recd_pointer = recd_pointer->next_num; } printf("Sum = %d\n", sum); return(0); }
The structure type record contains two members: the integer number and next_num, which is a pointer to a structure variable of type record.
The record type variables name1, name2, and name3 are assigned the following values:
The variable recd_pointer is a pointer to a structure of type record. It is initialized to the address of name1 (the beginning of the linked list).
The while loop causes the linked list to be scanned until recd_pointer equals NULL. The statement:
recd_pointer = recd_pointer->next_num;
advances the pointer to the next object in the list.
Related References
A union is an object similar to a structure except that all of its members start at the same location in memory. A union can contain the value of only one of its members at a time. The default initializer for a union with static storage is the default for the first component; a union with automatic storage has none.
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.
Only the first member of a union can be initialized.
Each union definition creates a new union type that is neither the same as
nor compatible with any other union type in the same source file.
However, a type specifier that is a reference to a previously defined union
type is the same type. The union tag associates the reference with the
definition, and effectively acts as the type name.
In C++, a union is a limited form of the class type. It can
contain access specifiers (public, protected, private), member data, and
member functions, including constructors and destructors. It cannot
contain virtual member functions or static data members. Default access
of members in a union is public. A union cannot be used as a base class
and cannot be derived from a base class.
C++ places additional limitations on the allowable data types for a union member. In C++, 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.
Related References
A union type definition contains the union keyword followed by an optional identifier (tag) and a brace-enclosed list of members.
A union definition has the following form:
>>-union--+-identifier--------------------------+-------------->< | .-----------. | | V | | '-+------------+--{----member--;-+--}-' '-identifier-'
A union declaration has the same form as a union definition except that the declaration has no brace-enclosed list of members.
The identifier is a tag given to the union specified by the member list. Once a tag is specified, any subsequent declaration of the union (in the same scope) can be made by declaring the tag and omitting the member list. If a tag is not specified, all variable definitions that refer to that union must be placed within the statement that defines the data type.
The list of members provides the data type with a description of the objects that can be stored in the union.
A union member definition has same form as a variable declaration.
A member of a union can be referenced the same way as a member of a structure.
For example:
union { char birthday[9]; int age; float weight; } people; people.birthday[0] = '\n';
assigns '\n' to the first element in the character array birthday, a member of the union people.
A union can represent only one of its members at a time. In the example, the union people contains either age, birthday, or weight but never more than one of these. The printf statement in the following example does not give the correct result because people.age replaces the value assigned to people.birthday in the first line:
#include <stdio.h> #include <string.h> union { char birthday[9]; int age; float weight; } people; int main(void) { strcpy(people.birthday, "03/06/56"); printf("%s\n", people.birthday); people.age = 38; printf("%s\n", people.birthday); }
The output of the above example will be similar to the following:
03/06/56 &
Related References
A union variable definition has the following form:
>>-+-------------------------+--union--union_data_type_name-----> '-storage_class_specifier-' >--identifier--+-------------------------+--------------------->< '-=--initialization_value-'
You must declare the union data type before you can define a union having that type.
You can initialize only the first member of a union.
The following example shows how you would initialize the first union member birthday of the union variable people:
union { char birthday[9]; int age; float weight; } people = {"23/07/57"};
You can define a union data type and a union of that type in the same statement by placing the variable declarator after the data type definition. The storage class specifier for the variable must appear at the beginning of the statement.
Related References
An anonymous union is a union without a class name. It cannot be followed by a declarator. An anonymous union is not a type; it defines an unnamed object and it cannot have member functions.
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.
For example, in the following code fragment, you can access the data members 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. A global
or namespace anonymous union must be declared with the keyword
static.
Related References
The following example defines a union data type (not named) and a union variable (named length). The member of length can be a long int, a float, or a double.
union { float meters; double centimeters; long inches; } length;
The following example defines the union type data as containing one member. The member can be named charctr, whole, or real. The second statement defines two data type variables: input and output.
union data { char charctr; int whole; float real; }; union data input, output;
The following statement assigns a character to input:
input.charctr = 'h';
The following statement assigns a floating-point number to member output:
output.real = 9.2;
The following example defines an array of structures named records. Each element of records contains three members: the integer id_num, the integer type_of_input, and the union variable input. input has the union data type defined in the previous example.
struct { int id_num; int type_of_input; union data input; } records[10];
The following statement assigns a character to the structure member input of the first element of records:
records[0].input.charctr = 'g';
Related References
An enumeration is a data type consisting of a set of values that are named integral constants. It is also referred to as an enumerated type because you must list (enumerate) each of the values in creating a name for each of them. A named value in an enumeration is called an enumeration constant. In addition to providing a way of defining and grouping sets of integral constants, enumerations are useful for variables that have a small number of possible values.
You can define an enumeration data type and all variables that have that enumeration type in one statement, or you can declare an enumeration type separately from the definition of variables of that type. The identifier associated with the data type (not an object) is called an enumeration tag. Each distinct enumeration is a different enumeration type.
In C, each enumerated type must be compatible with the integer type that
represents it. Enumeration variables and constants are treated by the
compiler as integer types. Consequently, in C you can freely mix the
values of different enumerated types, regardless of type compatibility.
C++ treats enumerated types as distinct from each other and from
integer types. You must explicitly cast an integer in order to use it
as an enumeration value.
Related References
An enumeration type declaration contains the enum keyword followed by an optional identifier (the enumeration tag) and a brace-enclosed list of enumerators. Commas separate each enumerator in the enumerator list. A declaration of an enumeration has the form:
.-,----------. V | >>-enum--+------------+--{----enumerator-+--}--;--------------->< '-identifier-'
The keyword enum, followed by the identifier, names the data type (like the tag on a struct data type). The list of enumerators provides the data type with a set of values.
In C, each enumerator represents an integer value. In C++, each enumerator represents a value that can be converted to an integral value.
An enumerator has the form:
>>-identifier--+---------------------------------+------------->< '-=--integral_constant_expression-'
To conserve space, enumerations may be stored in spaces smaller than that of an int.
When you define an enumeration data type, you specify a set of identifiers that the data type represents. Each identifier in this set is an enumeration constant.
The value of the constant is determined in the following way:
In C, enumeration constants have type int. If a constant
expression is used as an initializer, the value of the expression cannot
exceed the range of int (that is, INT_MIN to
INT_MAX as defined in the header
<limits.h>).
In C++, each enumeration constant has a value that can be promoted to a
signed or unsigned integer value and a distinct type that does not have to be
integral. Use an enumeration constant anywhere an integer constant is
allowed, or for C++, anywhere a value of the enumeration type is
allowed.
Each enumeration constant must be unique within the scope in which the enumeration is defined. In the following example, second declarations of average and poor cause compiler errors:
func() { enum score { poor, average, good }; enum rating { below, average, above }; int poor; }
The following data type declarations list oats, wheat, barley, corn, and rice as enumeration constants. The number under each constant shows the integer value.
enum grain { oats, wheat, barley, corn, rice }; /* 0 1 2 3 4 */ enum grain { oats=1, wheat, barley, corn, rice }; /* 1 2 3 4 5 */ enum grain { oats, wheat=10, barley, corn=20, rice }; /* 0 10 11 20 21 */
It is possible to associate the same integer with two different enumeration constants. For example, the following definition is valid. The identifiers suspend and hold have the same integer value.
enum status { run, clear=5, suspend, resume, hold=6 }; /* 0 5 6 7 6 */
Related References
An enumeration variable definition has the following form:
>>-+-------------------------+----------------------------------> '-storage_class_specifier-' >--enum--enumeration_data_type_name--identifier-----------------> >--+-------------------------+--------------------------------->< '-=--enumeration_constant-'
You must declare the enumeration data type before you can define a variable having that type.
The initializer for an enumeration variable contains the = symbol
followed by an expression enumeration_constant. In C++, the
initializer must have the same type as the associated enumeration type.
The first line of the following example declares the enumeration grain. The second line defines the variable g_food and gives g_food the initial value of barley (2).
enum grain { oats, wheat, barley, corn, rice }; enum grain g_food = barley;
The type specifier enum grain indicates that the value of g_food is a member of the enumerated data type grain.
The enum keyword is optional when declaring a variable with
enumeration type. However, it is required when declaring the
enumeration itself. For example, both statements declare a variable of
enumeration type:
enum grain g_food = barley; grain cob_food = corn;
Related References
You can define a type and a variable in one statement by using a declarator and an optional initializer after the type definition. To specify a storage class specifier for the variable, you must put the storage class specifier at the beginning of the declaration. For example:
register enum score { poor=1, average, good } rating = good;
C++ also lets you put the storage class immediately before the declarator
list. For example:
enum score { poor=1, average, good } register rating = good;
Either of these examples is equivalent to the following two declarations:
enum score { poor=1, average, good }; register enum score rating = good;
Both examples define the enumeration data type score and the variable rating. rating has the storage class specifier register, the data type enum score, and the initial value good.
Combining a data type definition with the definitions of all variables having that data type lets you leave the data type unnamed. For example:
enum { Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday } weekday;
defines the variable weekday, which can be assigned any of the specified enumeration constants.
Related References
The following program receives an integer as input. The output is a sentence that gives the French name for the weekday that is associated with the integer. If the integer is not associated with a weekday, the program prints "C'est le mauvais jour."
/** ** Example program using enumerations **/ #include <stdio.h> enum days { Monday=1, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday } weekday; void french(enum days); int main(void) { int num; printf("Enter an integer for the day of the week. " "Mon=1,...,Sun=7\n"); scanf("%d", &num); weekday=num; french(weekday); return(0); }
void french(enum days weekday) { switch (weekday) { case Monday: printf("Le jour de la semaine est lundi.\n"); break; case Tuesday: printf("Le jour de la semaine est mardi.\n"); break; case Wednesday: printf("Le jour de la semaine est mercredi.\n"); break; case Thursday: printf("Le jour de la semaine est jeudi.\n"); break; case Friday: printf("Le jour de la semaine est vendredi.\n"); break; case Saturday: printf("Le jour de la semaine est samedi.\n"); break; case Sunday: printf("Le jour de la semaine est dimanche.\n"); break; default: printf("C'est le mauvais jour.\n"); } }
Related References
(C) Copyright IBM Corporation 1992, 2006. All Rights Reserved.