32

We all have definitely used typedefs and #defines one time or the other. Today while working with them, I started pondering on a thing.

Consider the below 2 situations to use int data type with another name:

typedef int MYINTEGER

and

#define MYINTEGER int

Like above situation, we can, in many situations, very well accomplish a thing using #define, and also do the same using typedef, although the ways in which we do the same may be quite different. #define can also perform MACRO actions which a typedef cannot.

Although the basic reason for using them is the different, how different is their working? When should one be preferred over the other when both can be used? Also, is one guaranteed to be faster than the other in which situations? (e.g. #define is preprocessor directive, so everything is done way earlier than at compiling or runtime).

1
  • 9
    Use #define what they are designed for. Conditional compilation based on properties that you can not know when writing the code (like OS/Compiler). Use language constructs otherwise. Commented Jan 18, 2012 at 20:28

11 Answers 11

67

A typedef is generally preferred unless there's some odd reason that you specifically need a macro.

macros do textual substitution, which can do considerable violence to the semantics of the code. For example, given:

#define MYINTEGER int

you could legally write:

short MYINTEGER x = 42;

because short MYINTEGER expands to short int.

On the other hand, with a typedef:

typedef int MYINTEGER:

the name MYINTEGER is another name for the type int, not a textual substitution for the keyword "int".

Things get even worse with more complicated types. For example, given this:

typedef char *char_ptr;
char_ptr a, b;
#define CHAR_PTR char*
CHAR_PTR c, d;

a, b, and c are all pointers, but d is a char, because the last line expands to:

char* c, d;

which is equivalent to

char *c;
char d;

(Typedefs for pointer types are usually not a good idea, but this illustrates the point.)

Another odd case:

#define DWORD long
DWORD double x;     /* Huh? */
7
  • 1
    Pointers to blame again! :) Any other example besides pointers where it is unwise to use such macros?
    – c0da
    Commented Jan 18, 2012 at 11:29
  • 2
    Not that I'd ever use a macro in place of a typedef, but the proper way to build a macro that does something with whatever follows is to use arguments: #define CHAR_PTR(x) char *x. This will at least cause the compiler to kvetch if used incorrectly.
    – Blrfl
    Commented Jan 18, 2012 at 13:17
  • 1
    Don’t forget: const CHAR_PTR mutable_pointer_to_const_char; const char_ptr const_pointer_to_mutable_char;
    – Jon Purdy
    Commented Jan 18, 2012 at 17:16
  • 3
    Defines also make error messages incomprehensible Commented Jan 18, 2012 at 19:41
  • An example of the pain that misuse of macros can cause (though not directly relevant to macros vs. typedefs): github.com/Keith-S-Thompson/42 Commented Jan 19, 2012 at 21:47
14

By far, the biggest problem with macros is that they are not scoped. That alone warrants the use of typedef. Also, it is more clear semantically. When someone who reads your code sees a define, he won't know what it is about until he reads it and comprehends the entire macro. A typedef tells the reader that a type name will be defined. (I should mention that I'm talking about C++. I'm not sure about typedef scoping for C, but I guess it's similar).

3
  • But as I have shown in the example provided in the question, a typedef and #define use the same number of words! Also, these 3 words of a macro won't cause any confusion, as the words are the same, but just rearranged. :)
    – c0da
    Commented Jan 18, 2012 at 11:27
  • 2
    Yes, but it's not about shortness. A macro can do a zillion other things besides typedefing. But personally I think scoping is the most important. Commented Jan 18, 2012 at 11:29
  • 2
    @c0da - you should not be using macros for typedefs, you should be using typedefs for typedefs. The actual effect of the macro can be very different, as illustrated by various responses. Commented Jan 18, 2012 at 11:31
14

Source code is mainly written for fellow developers. Computers use a compiled version of it.

In this perspective typedef carries a meaning that #define doesn't.

0
6

Keith Thompson's answer is very good and together with Tamás Szelei's additional points about scoping should provide all the background you need.

You should always consider macros the very last resort of the desperate. There are certain things you can only do with macros. Even then, you should be thinking long and hard if you really want to do it. The amount of pain in debugging that can be caused by subtly broken macros is considerable, and will have you looking through preprocessed files. It's worth doing that just once, to get a feeling for the scope of the problem in C++ - just the size of the preprocessed file might be an eye-opener.

5

In addition to all valid remarks about scope and text replacement already given, #define is also not fully compatible with typedef! Namely when it comes to the case of function pointers.

typedef void(*fptr_t)(void);

This declares a type fptr_t which is a pointer to a function of the type void func(void).

You cannot declare this type with a macro. #define fptr_t void(*)(void) will obviously not work. You would have to write something obscure like #define fptr_t(name) void(*name)(void), which really doesn't make any sense in the C language, where we have no constructors.

Array pointers cannot be declared with #define either: typedef int(*arr_ptr)[10];


And while C has no language support for type safety worth mentioning, another case where typedef and #define is not compatible is when you do suspicious type conversions. The compiler and/or astatic analyser tool might be able to give warnings for such conversions if you use typedef.

1
  • 1
    Even better are combinations of function and array pointers, like char *(*(*foo())[])(); (foo is a function returning a pointer to an array of pointers to functions returning pointer to char).
    – John Bode
    Commented Feb 6, 2012 at 12:31
4

Use the tool with the least power that gets the job done, and the one with most warnings. #define is evaluated in the preprocessor, you are largely on your own there. typedef is evaluated by the compiler. Checks are given and typedef can only define types, as the name says. So in your example definitely go for typedef.

2

Beyond the normal arguments against define, how would you write this function using Macros?

template <typename IterType>
typename IterType::value_type Sum(
    const IterType& begin, 
    const IterType& end, 
    const IterType::value_type& initialValue)
{
    typename IterType::value_type result = initialValue;
    for (IterType i = begin; i != end; ++i)
        result += i;

    return result;
}

....

vector<int> values;
int sum = Sum(values.begin(), values.end(), 0);

This is obviously a trivial example, but that function can sum over any forward iterable sequence of a type that implements addition*. Typedefs used like this are an important element of Generic Programming.

*I just wrote this here, I leave compiling it as an excercise for the reader :-)

EDIT:

This answer seems to be causig a lot of confusion, so let me draw it out more. If you were to look inside of the definition of an STL vector, you would see something similar to the following:

template <typename ValueType, typename AllocatorType>
class vector
{
public:
    typedef ValueType value_type;
...
}

The use of typedefs within the standard containers allows a generic function (like the one I created above) to reference these types. The function "Sum" is templated on the type of the container (std::vector<int>), not on the type held inside the container (int). Without typedef it would not be possible to refer to that internal type.

Therefore, typedefs are central to Modern C++ and this is not possible with Macros.

5
  • The question asked about macros vs typedef, not macros vs inline functions.
    – Ben Voigt
    Commented Jan 21, 2012 at 2:05
  • Sure, and this is only possible with typedefs... that is what value_type is. Commented Jan 21, 2012 at 5:28
  • This isn't a typedef, it is template programming. A function or functor is not a type, no matter how generic it is.
    – user29079
    Commented Jan 23, 2012 at 7:51
  • @Lundin I've added further details: The function Sum is only possible because standard containers use typedefs to expose the types they are templated on. I am not saying that Sum is a typedef. I am saying std::vector<T>::value_type is a typedef. Feel free to look at the source of any standard library implementation to verify. Commented Jan 23, 2012 at 13:53
  • Fair enough. It would perhaps have been better to only post the second snippet.
    – user29079
    Commented Jan 23, 2012 at 15:20
1

typedef fits the C++ philosophy: all possible checks/asserts in compilation time. #define is just a preprocessor trick which hide a lot of semantic to the compiler. You should not worry about compilation performance more than code correctness.

When you create a new type you are defining a new "thing" that your program's domain manipulates. So you can use this "thing" to compose functions and classes, and you can use the compiler for your benefit to do static checks. Anyway, as C++ is compatible with C, there are a lot of implicit conversion between int and int-based types which do not generate warnings. So you don't get the full power of static checks in this case. However, you could replace your typedef with an enum to find implicit conversions. E.g.: If you've typedef int Age;, then you can replace with enum Age { }; and you will get all kind of errors by implicit conversions between Age and int.

Another thing: the typedef can be inside a namespace.

1

Using defines instead of typedefs is troubling under another important aspect, and namely the concept of type traits. Consider different classes (think of standard containers) all of which define their specific typedefs. You can write generic code by referring to the typedefs. Examples of this include the general container requirements (c++ standard 23.2.1 [container.requirements.general]) like

X::value_type
X::reference
X::difference_type
X::size_type

All of this is not expressible in a generic way with a macro because it is not scoped.

1

Don't forget the implications of this in your debugger. Some debuggers don't handle #define very well. Play around with both in the debugger you use. Remember, you'll spend more time reading it than writing it.

-2

"#define" will replace what you wrote after, typedef will create type. So if you want to have custome type - use typedef. If you need macros - use define.

1
  • I just love people who down voting and even don't bother to write a comment.
    – Dainius
    Commented Feb 20, 2015 at 9:00

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.