11.3. typedef DeclarationsThe easy way to use types with complex names, such as those described in the previous section, is to declare simple synonyms for them. In a declaration that starts with the keyword typedef, each declarator defines an identifier as a synonym for the specified type. The identifier is then called a typedef name for that type. Except for the keyword typedef, the syntax is exactly the same as for a declaration of an object or function of the specified type. Some examples: typedef unsigned int UINT, UINT_FUNC( ); typedef struct Point { double x, y; } Point_t; typedef float Matrix_t[3][10]; In the scope of these declarations, UINT is synonymous with unsigned int, and Point_t is synonymous with the structure type struct Point. You can use the typedef names in declarations, as the following examples show: UINT ui = 10, *uiPtr = &ui; The variable ui has the type unsigned int, and uiPtr is a pointer to unsigned int. UINT_FUNC *funcPtr; The pointer funcPtr can refer to a function whose return value has the type unsigned int. The function's parameters are not specified. Matrix_t *func( float * ); The function func( ) has one parameter, whose type is pointer to float, and returns a pointer to the type Matrix_t. Example 11-1 uses the typedef name of one structure type, Point_t, in the typedef definition of a second structure type. Example 11-1. typedef declarationstypedef struct Point { double x, y; } Point_t; typedef struct { Point_t top_left; Point_t bottom_right; } Rectangle_t; Ordinarily, you would use a header file to hold the definitions of any typedef names that you need to use in multiple source files. However, you must make an exception in the case of typedef declarations for types that contain a variable-length array. Variable-length arrays can only be declared within a block, and the actual length of the array is calculated anew each time the flow of program execution reaches the typedef declaration. An example: int func( int size ) { typedef float VLA[size]; // A typedef name for the type "array of float // whose length is (the value of size)." size *= 2; VLA temp; // An array of float whose length is the value // that size had in the typedef declaration. /* ... */ } The length of the array temp in this example depends on the value that size had when the typedef declaration was reached, not the value that size has when the array definition is reached. One advantage of typedef declarations is that they help to make programs more easily portable. Types that are necessarily different on different system architectures, for example, can be called by uniform typedef names. typedef names are also helpful in writing human-readable code. As an example, consider the prototype of the standard library function qsort( ): void qsort( void *base, size_t count, size_t size, int (*compare)( const void *, const void * )); We can make this prototype much more readable by using a typedef name for the comparison function's type: typedef int CmpFn( const void *, const void * ); void qsort( void *base, size_t count, size_t size, CmpFn *compare ); |