ILE C/C++ Language Reference

Compound Types

C++ 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

Structures

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.

C++ 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.

C++ 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.

Compatible Structures

C 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

Declaring and Defining a Structure

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.

C 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

Defining a Structure Variable

C 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.

C++ 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

Initializing Structures

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.

C 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:

Member
Value
 perm_address.street_no 
3
 perm_address.street_name 
address of string "Savona Dr."
 perm_address.city 
address of string "Dundas"
 perm_address.prov 
address of string "Ontario"
 perm_address.postal_code 
address of string "L4B 2A1"

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:

Member
Value
 temp_address.street_no 
44
 temp_address.street_name 
address of string "Knyvet Ave."
 temp_address.city 
address of string "Hamilton"
 temp_address.prov 
address of string "Ontario"
 temp_address.postal_code 
value depends on the storage class.
Note:
The initial value of uninitialized structure members like temp_address.postal_code depends on the storage class associated with the member.

Declaring Structure Types and Variables in the Same Statement

C 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

Declaring and Using Bit Fields in Structures

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++ 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

Example Program Using Structures

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:

Member Name
Value
 name1.number 
144
 name1.next_num 
The address of name2
 
 
 name2.number 
203
 name2.next_num 
The address of name3
 
 
 name3.number 
488
 name3.next_num 
NULL (Indicating the end of the linked list.)

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

Unions

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.

C Only the first member of a union can be initialized.

Compatible Unions

C 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.

C++ 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

Declaring a Union

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

Defining a Union Variable

C 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

Anonymous Unions

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
      }

C++ An anonymous union cannot have protected or private members. A global or namespace anonymous union must be declared with the keyword static.

Related References

Examples of Unions

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

Enumerations

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.

Compatible Enumerations

C 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++ 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

Declaring an Enumeration Data Type

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.

Enumeration Constants

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:

  1. An equal sign (=) and a constant expression after the enumeration constant gives an explicit value to the constant. The identifier represents the value of the constant expression.
  2. If no explicit value is assigned, the leftmost constant in the list receives the value zero (0).
  3. Identifiers with no explicitly assigned values receive the integer value that is one greater than the value represented by the previous identifier.

C 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>).

C++ 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

Defining Enumeration Variables

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.

C++ 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.

C++ 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

Defining an Enumeration Type and Enumeration Objects

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++ 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

Example Program Using Enumerations

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


[ Top of Page | Previous Page | Next Page | Table of Contents | Index ]