Industrial Training

Sub tleties of type def



Though novice C programmers don't find the typedef facility quite appealing professional C programmers use it quite often. As most of us know typedef merely adds a new name for some existing type. It does not create a new data type in any sense. For example,

typedef unsigned long int uli ;
uli i, j ;
unsigned long int x, y ;

Note that the variables i, j have exactly the same properties as variables x and y which have been declared explicitly. In effect, typedef is like #define, except that since it is interpreted by the compiler, it's reach is beyond the capabilities of the preprocessor.

There are three main reasons for using typedef s:

  1. It makes writing of complicated declarations a lot easier. This helps in eliminating a lot of clutter in the program.

  2. It helps in achieving portability in programs. That is, if we use typedefs for data types that are machine-dependent, only the typedefs need change when the program is moved to a new machine platform. One good example of this is to use typedef for various integer quantities (since their sizes change from machine to machine), then make an appropriate set of choices of short, int, and long for each host machine.

  3. It helps in providing a better documentation for a program. For example a node of a doubly linked list is better understood as ptrtolist rather than just a pointer to a complicated structure.

Now let's have a look at a few subtleties of typedef . Consider the following set of statements:

typedef char * string_t ;
#define string_d char *

string_t s1, s2 ;
string_d s3, s4 ;

Do you see any difference between using a typedef and a #define? In these declarations, s1, s2, and s3 are all treated as char *, but s4 is treated as a char, which is probably not the intention. Hence, in general, as compared to #define, typedef s are preferred because they can correctly encode pointer types. Also typedef s have the advantage that obey scope rules, that is, they can be declared local to a function or a block whereas #defines always have a global effect. Let us look at another example. This time we are trying to declare a pointer to a node in a linked list:

typedef struct

{

int data ;

NODEPTR link ;

} *NODEPTR ;

The compiler flags an error message for this declaration. Why? Can't a structure in C contain a pointer to itself? Of course it can. The problem with the declaration here is the typedef . In simpler cases we can define a new structure type and a typedef for it at the same time, but not in this case. A typedef declaration cannot be used until it is defined, and in our example it is not yet defined at the point where the link field is declared.

We can fix this problem in three ways:

  1. Give the structure a name, say node and then declare the link field as a simple struct node * as shown below:
    typedef struct node
    {
    int data ; struct node *link ;
    } *NODEPTR ;

  2. Keep the typedef declaration separate from the structure definition:
    struct node
    {
    int data ;
    struct node *link ;
    } ;
    typedef struct node * NODEPTR ;

  3. Precede the structure declaration with typedef, so that you can use the NODEPTR typedef when declaring the link field:
    typedef struct node *NODEPTR ;

    struct node
    {
    int data ;
    NODEPTR link ;
    } ;

In this case, we have declared a new typedef name involving struct node even though struct node has not been completely defined yet; this is allowed by the compiler.

We know that C permits us to define self-referential structures. That is, within the definition of a structure we can have a pointer to a structure of the same type. In fact using this capability of structures we are able to implement data structures like linked list, binary trees, etc. Can we define a pair of mutually referential structures as shown below?

typedef struct {   		int data1 ;  
BPTR link1 ;
} *APTR ; typedef struct { int data2 ; APTR link2 ; } *BPTR ;

The problem with the above code is the compiler doesn't know about BPTR when it is used in the first structure declaration. The problem lies not in the structures or the pointers but in the typedef s. We are violating the rule that a typedef declaration cannot be used until it is defined, and in our example it is not yet defined at the point where the link1 field is declared. To avoid this problem we can define the structures as shown below:

struct a   {       int data1 ;       struct b *link1 ;  } ;    struct b  {       int data2 ;       struct a *link2 ;  } ;    typedef struct a *APTR ;  typedef struct b *BPTR ;  

The compiler can accept the field declaration struct b *ptr1 within struct a, even though it has not yet heard of struct b (which is "incomplete" at that point). Occasionally, it is necessary to precede this couplet with the line

struct b ;

This empty declaration masks the pair of structure declarations (if in an inner scope) from a different struct b in an outer scope. After declaring the two structures we can then declare the typedef s separately as shown above. Alternatively, we can also define the typedefs before the structure definitions, in which case you can use them when declaring the link pointer fields as shown below:

typedef struct a *APTR; 
typedef struct b *BPTR;
struct a
{
int data1 ;
BPTR link1 ; } ;
struct b
{
int data2 ;
APTR link2 ;
} ;
Can you figure out what the following  complicated  declarations mean?
typedef char *pc ;  
typedef pc fpc( ) ;
typedef fpc *pfpc ;
typedef pfpc fpfpc( ) ;
typedef fpfpc *pfpfpc ; pfpfpc a[ N ] ;

What does pfpfpc a[N] represent? It is an array of N pointers to functions returning pointers to functions returning pointers to characters. Without the typedef we can declare the same thing as: char * ( * ( * a [ N ] ] ) ( ) ) ( ) ; Obviously using typedef s is easier than using this complicated declaration. If you look at the typedef s carefully you can analyze them as follows:

pc          is a pointer to char
fpc         is function returning pointer to char
pfpc       is pointer to a function returning pointer to char
fpfpc      is a function returning pointer to a function returning pointer to char
pfpfpc    is a pointer to function returning pointer to a function returning pointer to char
pfpfpc    is a[ N ] is an array of N pointers to functions returning pointers to functions returning pointers to characters

Hi I am Pluto.