I am newby in programming in C language. Can someone explain me how to "read" declarations like *(float *)a
and give some example step by step.
Thanks!
I am newby in programming in C language. Can someone explain me how to "read" declarations like *(float *)a
and give some example step by step.
Thanks!
*(float *)a
is not a declaration; it is a cast expression. In this particular case, it's taking the value of a
, treating it as a pointer to float
, and dereferencing the result. Most likely a
is a pointer to a different type, and we want to treat it as a pointer to float
.
As far as pointer declarations are concerned...
A simple pointer declaration looks like
T *p; // p is a pointer to T
where T
is any type (potentially with qualifiers like const
or volatile
). The type of the object p
is "pointer to T
"; the type is fully specified by the combination of the type specifiers and qualifiers in T
and the declarator *p
.
The declarator introduces the name of the thing being declared, along with any type information not provided in the type specifier. In the declaration above *p
is the declarator. It provides the name of the object (p
) and any additional type information (pointer to). Note that even if you write the declaration as
T* p;
it will be parsed as
T (*p);
The same thing holds for declarations of arrays and functions:
T a[N]; // a is an N-element array of T
T f(); // f is a function returning T
The array-ness of a
is given by the declarator a[N]
and the function-ness of f
is given by the declarator f()
.
You can combine *
with []
and ()
, like so:
T *a[N]; // a is an N-element of pointers to T
T (*a)[N]; // a is a pointer to an N-element array of T
T *f(); // f is a function returning pointer to T
T (*f)(); // f is a pointer to a function returning T
In both declarations and expressions, the unary *
has lower precedence than either postfix []
or ()
, so *a[N]
and *f()
are parsed as *(a[N])
and *(f())
. If you want a
to be a pointer to an array instead of an array of pointers, you must explicitly group the *
with a
as (*a)[N]
.
You can further combine these elements like so:
T **p; // p is a pointer to a pointer to T
T a[N][M]; // a is an NxM array of T
T (*f[N])(); // f is an array of pointers to functions returning T
T (*f())[N]; // f is a function returning a pointer to an array of T
T *(*(*a[N])())(); // a is an array of pointers to functions returning pointers to functions returning pointers to T
Yes, functions can return pointers to arrays and pointers to other functions, and yes, the syntax looks funky, but if follows logically from how declarators work. You just substitute the function call (along with any parameters) for the pointer name:
T (*a)[N] => T (*f())[N], a => f()
T (*f)() => T (*g())(), f => g()
Here's an example: the signal
function from the C standard library. It's a function that returns a pointer to another function:
void (*signal(int sig, void (*func)(int)))(int);
To read this declaration, start with the leftmost identifier and work your way out, applying the precedence rules given above. Apply those rules recursively to any function parameters:
signal -- signal
signal( ) -- is a function taking
signal( sig ) -- parameter sig
signal(int sig ) -- is an int
signal(int sig, func ) -- parameter func
signal(int sig, (*func) ) -- is a pointer
signal(int sig, (*func)( )) -- to a function taking
signal(int sig, (*func)( )) -- unnamed parameter
signal(int sig, (*func)(int)) -- is an int
signal(int sig, void (*func)(int)) -- returning void
*signal(int sig, void (*func)(int)) -- returning a pointer
(*signal(int sig, void (*func)(int)))( ) -- to a function taking
(*signal(int sig, void (*func)(int)))( ) -- unnamed parameter
(*signal(int sig, void (*func)(int)))(int) -- is an int
void (*signal(int sig, void (*func)(int)))(int); -- returning void
So, signal
is a function that returns a pointer to a function. signal
takes two parameters, one a plain int
, the other a pointer to a function that takes an int
and returns void. Note that in a function declaration, parameter names don't have to be specified. They will need to be specified in the corresponding function definition.
In general, you have to care about both the precedence and the associativity of operators: table
In this case, the dereference and the cast operators have the same precedence and are both right-to-left associatives. This means that each operator apply to what is on its right side, and since they have the same precedence, the dereference operator apply after the cast operator, which is on its right side.
So the sequence of operations performed is: access a -> cast it to (float *) -> dereference it.
EDIT: in the case you have an unary operator (dereference, cast, negation, etc.), that operator is always right-to-left associative and it applies after all the expression at its right side as been calculated. So, in our case you can say that the operators are applied from right to left also without looking at the above table.