tags:

views:

260

answers:

8

I was curious to know how exactly typedef works.

typedef struct example identifier;

identifier x;

In above statement is 'identifier' just replaced (somethings like string replacement) with 'struct example' in code? If no, what does typedef do here?

please enlighten!

A: 

Typedef is a shortcut which creates new name for (usually complex) type. It's purpose is more narrow than preprocessor's string replacement. Thus, it is less error-prone than preprocessor definitions (which are parsed recursively).

alxx
A: 

With typedef you make an alias. The compiler replaces the alias with the correct code.

If you write:

typedef int company_id;
company_id mycompany = 100;

The compilers gets:

int mycompany = 100;
Victor Marzo
It's not string replacement and has nothing to do with the preprocessor. The compiler gets `company_id`.
ptomato
I hadn't say that it's a preprocessor directive like #define. I've say that is to make an alias from a type.
Victor Marzo
I think the problem is with the line 'The compiler gets'. The compiler gets the real code exactly as you have written it. It *interprets* it as if the original type was used there though --with some slight differences.
David Rodríguez - dribeas
+8  A: 

A typedef introduces a synonym for types. It isn't plain string replacement, as the following shows:

typedef int* int_ptr;
const int* a;    // pointer to const int
const int_ptr b; // const pointer to int

The compiler also is aware that it is a type name, you can't just put it somewhere where a type isn't allowed without getting a compiler error.

Georg Fritzsche
+27  A: 

No, it is not a string replacement - that would be macros. It creates an alias for the type.

typedefs are preferred over macros for custom types, in part because they can correctly encode pointer types.

typedef char *String_t;
#define String_d char *
String_t s1, s2;
String_d s3, s4;

s1, s2, and s3 are all declared as char *, but s4 is declared as a char, which is probably not the intention.

Amarghosh
Nice example. +1
spender
+1 for the answer and especially the good example on how a string replacement would lead to other behaviour
schnaader
Well, I just copied it from c-faq :)
Amarghosh
Thanks @Amar for neat explanation :)
Kedar
There are other issues with how the different identifiers are handled by the compiler. In particular, typedef-ed names and user defined types are kept in different identifier spaces. I have added that as an answer below.
David Rodríguez - dribeas
nice example!! +1
mkamthan
A: 

Macros are done by the preprocessor and are purely based on text substitution. Thus, you could say they're pretty dumb. The preprocessor will accept pretty much any garbage without any syntax check.

Typedefs are done by the compiler itself and they manipulate the compiler's own type table by adding a derived type you define. This is subject to full syntax checking, and a mechanism specifically for types only.

You could think of the compiler doing similar "work" to when you declare a struct. There's a definition there, and the compiler turns it into a type in its list of types.

Carl Smotricz
+3  A: 

All agree in that it is not type substitution, and that it is much better than it when pointers get into the mix, but there are also other subtleties. In particular the use of typedefs can affect how code is parsed and the validity of programs.

Both in C and in C++ user defined identifiers are kept in different name spaces (not in the C++ sense, but some kind of identifier-space). When you use the typedef keyword you make an alias for the type in the global name space, where functions reside.

// C/C++
struct test {};
void test( struct test x ) {} // ok, no collision

// C/C++
typedef struct test {} test; // test is now both in types and global spaces
//void test() {}       // compiler error: test is a typedef, cannot be redefined

A slight difference here is that in C++ the compiler will first look in the global name space and if not found there it will also look in the types name space:

// C
struct test {};
//void f( test t );      // error, test is not defined in the global namespace
void f( struct test t ); // ok, we are telling the compiler where to look

// C++
struct test {};
void f( test t );        // ok, no test defined in the global name space, 
                         // the compiler looks in the types name space
void g( struct test t ); // also ok, even if 'struct' not needed here.

But this does not mean that the two namespaces are merged, only that the lookup will search in both places.

David Rodríguez - dribeas
+1 Thanks for that!
Amarghosh
+2  A: 

One important distinction is that typedefs have scope.

The following is a common idiom

class Foo: public Bar 
{
  private:
   typedef Bar inherited;

  public:
    Foo(int x) : inherited(x) {};  // preferred to 'Bar(x)' 
}

Often you would have the constructor definition in a .cpp file, with the declaration in header. If you use Foo(int x) : Bar(x), there's a good chance you will forget to update the constructor if you change the class hierarchy such that Foo->Wibble->Bar instead of Foo->Bar. Personally I recommend adding the 'inherited' typedef to every subclass.

See here for more details: http://stackoverflow.com/questions/180601/using-super-in-c

Roddy
I've never coded this way and I've never seen code coded this way...
strager
@strager: Wait until you have to inherit from `some_template<a, other_template<b>::type, /* ... more? */>`.
Georg Fritzsche
+1 for mentioning scope.
Tadeusz Kopec
@Georg Fritzsche, Ah, I have seen (and written) that, yes. Your example makes it look like it's done for all cases, though (especially the "preferred to 'Bar(x)'" comment).
strager
A: 

I find typedefs make function signatures a lot easier to read. Suppose you want to return a pointer to a two-dimensional array. Here is the readable way:

typedef int three_by_three[3][3];

three_by_three* foo();

And here is how you can do it without a typedef:

int (*bar())[3][3];

Note that this signature does does not look at all like the first version with a "string replacement" applied.

If the C declarator syntax wasn't that ugly (Stroustrup: "I consider the C declarator syntax an experiment that failed"), typedefs probably wouldn't by used as much as they are now.

FredOverflow