When you declare a pointer to int:
int *a, b;
a
is a pointer to int
whereas b
is an int
(I've never really understood why this happens, I know is related to the comma but I used to say that the asterisk is binned to the variable name instead of data type). Anyway, if you:
#define intptr int*
intptr a, b
The result is the same. The fact that I find weird is why using typedef
for the same thing changes the behavior?
typdef int* intptr
intptr a, b;
In the last example I was expecting a
to be an int*
whereas b
an int
. Instead, they are both int*
For clarification, having the following code snippet I don't understand why I don't get the same warning for the d
variable
#define intptr_def int*
typedef int* intptr_typ;
int main()
{
int x;
intptr_def a, b;
intptr_typ c, d;
a = &x;
b = &x; // Warning assignment to 'int' from 'int *' makes integer from pointer without a cast
c = &x;
d = &x;
return 0;
}
A macro is a text substitution, nothing more. If you have
#define intptr int*
and write
intptr a, b;
then when the code is preprocessed, the intptr
macro is expanded to
int* a, b;
which is processed exactly the same as
int *a, b;
and only declares a
as a pointer; b
is a regular int
.
A typedef name is an alias for a type - it’s not a text substitution like the preprocessor macro.
typedef int *intptr;
creates intptr
as an alias for int *
(pointer to int
).
So when you write
intptr a, b;
both a
and b
have type intptr
(int *
).
Declarations in C have the basic structure
declaration-specifiers declarators
Declaration specifiers include type specifiers (int
, float
, char
, etc.), type qualifiers (const
, volatile
), storage class specifiers (static
, auto
, register
), and a few other specifiers related to function declarations/definitions.
A declarator introduces the name of the thing being declared, along with information about that thing’s array-ness, pointer-ness, and function-ness. For example, in the declaration
static volatile unsigned int a[10], *p;
the declaration specifiers are static volatile unsigned int
and the declarators are a[10]
and *p
. The type of each variable is fully specified by the combination of the declaration specifiers and the declarator. The type of a
is "10-element array of static volatile unsigned int
" and the type of p
is "pointer to static volatile unsigned int
".
The idea is that the structure of the declarator matches the structure of the expression in the code. For example, suppose you have a pointer to an int
named iptr
, and you want to print the value in the object it points to, you’d write
printf( "%d\n", *iptr );
The type of the expression *iptr
is int
, so the declaration is written as
int *iptr;
You can also write it as
int* iptr;
or
int*iptr;
or even
int * iptr;
and all will be interpreted as
int (*iptr);
Whitespace doesn’t make any difference - the *
is always part of the declarator, not the declaration specifiers. Declarators can get arbitrarily complex - for any sequence of declaration specifiers T
, you can have
T x; // x is an object of type T
T a[N]; // a is an array of T
T *p; // p is a pointer to T
T f(); // f is a function returning T
T *a[N]; // a is an array of pointer to T
T (*p)[N]; // p is a pointer to an array of T
T *f(); // f is a function returning pointer to T
T (*p)(); // p is a pointer to a function returning T
T *(*a[N])(); // a is an array of pointers to functions
// returning pointer to T
and that just barely scratches the surface.