John posted:
typedef char byte ; // make byte an alias for char
Indeed. The syntax is conveniently identical to the definition of an object
or function:
char (*byte(int))[50];
"byte" is a function which returns a pointer to an array of fifty char's.
typedef char (*byte(int))[50];
"byte" is the type of a function which returns a pointer to an array of
fifty char's.
typedef string *pstring ;
Well, if we were defining an object, the type of "pstring" would be:
string*
That is: Non-const pointer to non-const string. If we had a "nonconst"
keyword, we could write it as:
string nonconst *nonconst
or:
nonconst string *nonconst
Here we have a const pstring. The type of "cstr" is:
string *const
No matter how many times I read the explanation in Lippman's book, I'm
still lost. Since typedefs are clearly not simple aliases as all of the
other books claim, I'm hoping that someone could help me understand the
above.
If you have a definition which is "simple" (i.e. not a pointer, not an
array, not a reference, not a function), then you define it as follows:
int i;
If you want the object to be const, then you write "const" either before or
after the type:
int const i;
const int i;
If you want a more "complicated" definition, then things become different.
You start off with the fundamental type, which includes the constness and
volatility, for example:
int const
, next you write its name:
int const i;
Now, here's where things get special. Having already specified the
"fundamental type", the way you specify more information for a "special
definition" is by messing with the name. For instance, let's turn it into a
pointer:
int const *p;
From now, the "int const" doesn't apply to "p" at all, because we're
dealing with a special definition -- the "int const" simply specifies the
"fundamental type". At the moment, "p" is non-const. If we want to make it
const, we mess with the name again:
int const *const p;
(You can think of its name as being "*const p".)
The whole idea of an object's name having an effect on its type is
reflected in the following multiple definition:
int *p, *q, r[5], (*s)[5], *t[5], Func(), *Func2();
p is a pointer.
q is a pointer.
r is an array of five.
s is a pointer to an array of five.
t is an array of five pointers.
Func is a function which returns an int.
Func2 is a function which returns a pointer to an int.
A typedef doesn't do simple text replacement -- if you want that, then use
#define. Instead, it gives another name for a fully-fledged fundamental
type. A fundamental type includes "constness" and "volatility".
Looking at that object definition again:
const pstring cstr;
We see that the fundamental type is "pstring". We know that a "pstring" is
a non-const pointer to a non-const string.
When defining an object, you can place the "const" before or after the
fundamental type, so the following two are equivalent:
const pstring cstr;
pstring const cstr;
So you see: Once you make a typedef, you've made a full-fledged fundamental
type which can't be hacked at.
(I hope I explained that OK. It's one of those things that experienced
programmers simply just understand and accept, but which is hard to get
across to beginners.)