The Typedef Keyword in C and C++

By Alex Allain
The typedef keyword allows the programmer to create new names for types such as int or, more commonly in C++, templated types--it literally stands for "type definition". Typedefs can be used both to provide more clarity to your code and to make it easier to make changes to the underlying data types that you use.

What is a type? No, really, what does it mean?

One purpose of having types is to make sure that variables are always used in the way that they were intended to be used. C and C++ provide several built-in types that are based on the underlying representation of the data. Sometimes this is good enough; when you're working to save memory or perform numerical computations that require the right level of precision, you want to have tight control over these matters.

On the other hand, many times, you don't care so much about the underlying representation of data--is it a float or an int or a double--as much as you care about how it will be used. For instance, you might have some variables that always store information about the user's current score in a game or how many points the player can get from performing an action like killing an enemy. You might also have some variables that are intended to keep track of the amount of life left for the player and how many life points he can receive for picking up life-enhancers. These two concepts are totally different, and there's no time when you'd ever want to store one type of data--for instance, the number of points--in a variable that is intended for the other type--the amount of life points a player has or can gain.

You know from the start that these two concepts are totally different, even if you store both of them as integers. By thinking of the two types of data as different "data types", you give yourself a useful way to add an element of defensive programming to your code by making it clear what type each variable is associated with--for example, you might prefix all variables storing information about life with an l, and about scores with an s. You'd never expect an assignment from lhigh to shigh--the two concepts are fundamentally incompatible.

One way of actually enforcing this constraint is to actually create "wrapper" classes for the different types. This allows your compiler to spot incorrect assignments and prevent you from making them.

The Role of Typedefs

Another partial solution to this problem is to use typedefs. This won't give you quite as much safety as having wrapper classes, but it does mean that you can at least make it clear what a function is supposed to return. If you see
life_t get_high();
then you know that get_high returns the highest possible life value your player can have, not the high score. Of course, the naming here is a bit suspect--you could have written the function name as get_high_life instead. But having the return type as a life_t makes it extra-clear what should be returned. In cases where you have a function return multiple values by both returning a value and setting memory passed in via pointer, this can be especially useful:
error_t get_data( char *data );
It's now clear that the get_data function returns some kind of error type, not the data that presumably will be returned via the data pointer.

Typedefs help you here because can be used to give more specific names for built-in types: for instance, you will often see the size_t type used instead of a plain old unsigned int when it's useful to make clear that the value is expected to be used as a size--for example, the strlen function returns a "size_t" type instead of an unsigned int.


So, how do you actually declare a typedef? All you must do is provide the old type name followed by the type that should represent it throughout the code. Here's how you would declare size_t to be an unsigned integer:
typedef unsigned int size_t;
From here on out, you would be able to use size_t instead of unsigned int. Note that in C, typedefs can also be used to remove some of the burden associated with declaring structs. In C, struct variables must be declared by a combination of the keyword struct and the name of the struct:
struct my_struct_type my_struct_variable;
This can be annoying, so some programmers use typedef to create shorter names:
typedef struct my_struct_type my_short_type_t;

Typedefs and Abstraction

Typedefs provide a level of abstraction away from the actual types being used, allowing you, the programmer, to focus more on the concept of just what a variable should mean. This makes it easier to write clean code, but it also makes it far easier to modify your code. For instance, if if you decided you really needed to support sizes that were too big to store in an unsigned int, you could make a change in one place in your code--the typedef itself--to make size_t equivalent to, for instance, an unsigned long. Almost none of your code would need to change!

This method of using typedefs is can also be a convenience; C++ template types can be exceptionally long and annoying to type, especially when making heavy use of the STL. Typedefs allow you to easily create abbreviations for these long type names, with the added bonus that they make it much easier to make changes throughout your code if you want to change the templated type.

For example, if you have a map of strings to ints, you could declare it as
std::map<string, int>
everywhere in your code. But this is a lot of typing, and annoying punctuation. Moreover, it doesn't give you any information about what it actually means. Using a typedef here saves both typing and adds clarity.
typedef std::map<string, int> ScoreByName;
Now it's clear that this map is designed to store the scores associated with different individuals. Moreover, if you decided that you wanted scores to be floating point numbers, you'd have no trouble changing it from mapping scores as integers to scores as floating point numbers.


The good

  • Typedefs can make your code more clear
  • Typedefs can make your code easier to modify

The gotchas

  • One thing to keep in mind when using typedefs is that the underlying type might matter; avoid typedefs, or make them easy to find, when it's important to know the underlying data type

Related Articles

Enumerated types can make your code easier to understand and more type safe

How and why to comment discusses using comments to increase readability