1. | Welcome | ToC | FAQ | Resources | Courses | Projects | Mail Lists | Members | Misc |
2. | Fundamentals | Languages | Tools | Net | Core | Advanced |
3. | EoC | Java |
4. | Lessons |
5. | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 |


Essence of C
7 - CUSTOM DATA TYPES

Structures, Bit-Fields, Unions, Enumerations, typedef

C allows the programmer to create custom data types in five different ways. These are:

Structures

A structure is a collection of variables referenced under one name, providing a convenient means of keeping related information together.


Definition

A structure definition forms a &quottemplate"which may be used to create structure variables. The variables which make up a structure are called structure elements.

In the following, the C keyword struct tells the compiler that a structure template is being defined:

     struct addr
     {
       char name[30];
       char street[40];
       int postcode;
     };

In this example, addr is known as the structure tag. Note that a structure definition is actually a statement; hence it is terminated by a semi-colon. Note also that only the form of the data has been defined at this stage and that no variable has yet been declared.


Declaration

An example of a structure variable declaration of type addr is:

 

     struct addr addr_info;

Variables may also be declared as the structure is defined, as in:

     struct addr
     {
       char name[30];
       char street[40];
       int postcode;
     } addr_info, binfo, cinfo;

If only one variable is needed, the structure tag is not needed, e.g.:

     struct
    {
      char name[30];
      char street[40];
      int postcode;
    } addr_info;


Referencing Structure Elements

Individual elements of a structure are referenced using the dot operator, e.g.:

     addr_info.postcode = 1234;
     printf("%d",addr_info.postcode);


Structure Assignments

ANSI C compilers allow the information in one structure to be assigned to another structure, as in:

     binfo=addr_info;


Arrays of Structures

The following example declares a 100-element array of type addr:

     struct addr addr_arr[100];

in which the postcode element of structure 3 would be accessed by:

     printf("%d", addr_info[2].postcode);


Passing Structures to Functions

When an element of a non-global structure is to be passed to a function, the value of that element is passed (unless that element is complex, such as an array of characters). For example, given:

     struct fred
     {
       char x;
       int y;
       char z[10];
     } mike;

each element would be passed like this:

    funct(mike.x);            // passes character value of x
    funct(mike.y);            // passes integer value of y
    funct(mike.s);            // passes address of string
    funct(mike.s[2]);         // passes character val of s[2]

If the address of the element is required to be passed, the & operator is placed before the structure name. In the above example, this would apply except in funct(mike.s);.


Passing Entire Structures to Functions

When a structure is used as an argument to a function, the entire structure is passed using the standard call-by-value method. Of course, this means that changes made to the contents of the structure inside the function do not affect the structure used as the argument.


Declaring Structure Pointers

Assuming the previously defined structure addr, the following declares addr_pointer as a pointer to data of that type:

    struct addr *addr_pointer;


Using Structure Pointers

Structure pointers may be used to generate a call-by-reference to a function and to create linked lists and other dynamic data structures. Calls-by-reference should be used for all but the simplest structures so as to avoid overheads occasioned by the pushing and popping of all the structure elements onto and off the stack.

To access the elements of a structure using a pointer, the -> operator is used, as in:

    ptr->postcode

where ptr has been declared as a pointer to the type of structure and assigned the address of a variable of that type, and postcode is a structure element within that variable.


Arrays and Structures Within Structures

Structure elements may be arrays and structures. When a structure is an element of another structure, it is called a nested structure.

Bit-Fields

Preamble. Unlike most other languages, C has a built-in method for accessing a single bit within a byte. This can be useful because:

Tasks involving the manipulation of bits may, of course, be performed using C's bitwise operators. However, the bit-field provides a means of adding more structure and efficiency to the coding.


Bit-Fields - A Type of Structure

To access bits using bit-fields, C uses a method based on the structure. In fact, a bit-field is simply a special type of structure element which defines how long, in bits, the field is to be. The general form of a bit-field definition is:

    struct tag
    {
      type name1: length;
      type name2: length;
      type name3: length;
    } variable_list;

A bit-field must be declared as either int, unsigned, or signed. Bit-fields of length 1 should be declared as unsigned because a single bit cannot have a sign.


An Example Application

Bit-fields are frequently used when analyzing input from a hardware device. For example, the status port of a serial communications device might return a status byte like this:

Bit

Meaning When Set

0 Change in clear-to-send line.
1 Change in data-set-ready.
2 Trailing edge detected.
3 Change in receive line.
4 Clear-to-send.
5 Data-set-ready.
6 Telephone ringing.
7 Received signal.

Defining the Bit-Field. The foregoing can be represented in a status byte using the following bit-field definition/declaration:

    struct status_type
    {
      unsigned delta_cts: 1;
      unsigned delta_dsr: 1;
      unsigned tr_edge:   1;
      unsigned delta_rec: 1;
      unsigned cts:       1;
      unsigned dsr:       1;
      unsigned ring:      1;
      unsigned rec_line:  1;
    } status;

Using this bit-field, the following routine would enable a program to determine whether it can send or receive data:

    status = get_port_status;

    if(status.cts)
      printf("Clear to send");
    if(status.dsr)
      printf("Data set ready);


Referencing Bit-Field Elements

Values are assigned to a bit-field using the usual method for assigning values to structure elements, e.g.:

    status.ring = 0;

As with structures, if an element is referenced through a pointer, the -> operator is used in lieu of the dot operator.


Variations in Definition

All bit-fields need not necessarily be named, which allows unused ones to be bypassed. For example, if, in the above example, access had only been required to cts and dsr, status_type could have been declared like this:

    struct status_type
    {
      unsigned:     4;
      unsigned cts: 1;
      unsigned dsr: 1;
    } status;


Limitations

Bit-field variables have certain limitations. For example:


Mixing Normal and Bit-Field Structure Elements

Normal and bit-field structure elements may be mixed, as in:

    struct emp
    {
      struct addr address;
      float pay;
      unsigned lay-off: 1; // lay-off or active
      unsigned hourly:  1; // hourly pay or wage
      unsigned deducts: 3; // tax deductions
     }

which demonstrates the use of only one byte to store information which would otherwise require three bytes.

Unions

A union is a memory location which is shared by two or more different variables, generally of different types, at different times. The general form of a union definition is:

    union tag
    {
      type variable_name;
      type variable_name;
      type variable_name;
     .
     .
    } union_variables;

An example definition is as follows:

    union u_type
    {
      int i;
      char ch;
    };

Declaration options are as for structures, ie, variable names may be placed at the end of the definition or a separate declaration statement may be used.

In the example shown, both the integer i and the character ch share the same memory location, even though ch only requires the first of the bytes required by i.


Accessing Union Elements

Union elements are accessed using the same methods used for accessing structure elements.


Usage

The union aids in the production of machine-independent code because the compiler keeps track of the actual sizes of the variables which make up the union.

Unions are frequently used when type conversions are needed because the data held in a union can be referred to in different ways.

Enumerations

An enumeration is a set of named integer constants which specify all the legal values a variable of that type may have. Enumerations are common in everyday life. For example, an enumeration of the coins in a currency might be:

    10 cents, 25 cents, 50 cents, dollar


Definition and Declaration

Enumerations are defined much like structures. An example definition is as follows:

    enum coin { ten_cent, quart_doll, half_doll, dollar };

An example declaration is:

    enum coin money;

Given this definition and declaration, the following statements are valid:

    money = quart_doll;

    if(money == quart_doll)
     printf("is 25 cents\n");

In an enumeration, each symbol stands for an integer value. For example, using the above definition and declaration:

    printf("%d %d", ten_cent, dollar);

displays 0 3 on the screen.


Initialization

The symbols in an enumeration may be initialized, as in the following example:

    enum coin { ten_cent, quart_doll, half_doll=100, dollar };

which would place these values in the symbols:

    ten_cent   0
    quart_doll 1
    half_doll  100
    dollar     101

To avoid the rather tedious coding arising from the fact that symbols are integers and not strings, an array of strings may be declared for access by the enumeration, e.g.:

    char name[] =
    {
      "10 cents",
      "25 cents",
      "50 cents",
      "dollar"
    }

    money = quart_doll;
    printf("%s", name[money]);

Note that this only works if no symbol is initialized, the reason being that the string array must be indexed starting at 0.

typedef

typedef allows a new data type to be explicitly defined. Note that it does not actually create a new data class, but simply defines a new name for an existing type. This process helps in making machine-dependent code more portable, since only the typedef statements need be changed. It can also aid in self-documenting a program by allowing the use of more descriptive names for the standard data types.

For example, a new name for float could be created as follows:

    typedef float balance;

which would allow a new float variable to be created by:

    balance overdue;

Prior Chapter · Index · Next Chapter


Copyright © 1996, 1997, 1998. Last Update to This Page: 1998/10/24
This Page Maintained by: radar pangaean * * * Original Author: K.J.Bricknell
The MOST web site is built and maintained by the voluntary efforts/donations of our members.