Reputation: 49
I was studying C99 - Function pointer part and my textbook offered a way to use 'typedef' for simplifying the code below :
int (*p[4])(int,int) = {&Sum,&Sub,&Mul,&Div};
↓
typedef int (*OP_TYPE)(int,int);
OP_TYPE p[4]={&Sum,&Sub,&Mul,&Div};
I've known that 'typedef' can be used only like this : eg.
typedef unsigned short int US;
Was it not wrong, int
would be altered as (*OP_TYPE)(int,int)
, and Isn't it right the second sentence become (*OP_TYPE)(int,int)(*p[4])(int,int) = {&Sum,&Sub,&Mul,&Div};
I can't understand why the second sentence could be like that.
Upvotes: 4
Views: 256
Reputation: 4539
OP_TYPE is a function pointer type. Here is an example:
// This is a function
// It has a single parameter and returns
// an int
int some_func(int a) {
return(a+1);
}
int main(void) {
// regular usage of the function
printf("some_func(1) = %d\n", some_func(1));
// fun_ptr is a function pointer
// that points to the function some_func
int (*fun_ptr)(int) = &some_func;
printf("via fun_ptr->some_func(2) = %d\n", fun_ptr(2));
// create the typedef
typedef int (*funptr_type)(int);
// usage of typedef
funptr_type another_func_ptr = &some_func;
// usage of function pointer via typedef
printf("via typedef some_func(3) = %d\n", another_func_ptr(3));
return(0);
}
Usage
some_func(1) = 2
via fun_ptr->some_func(2) = 3
via typedef some_func(3) = 4
Upvotes: 0
Reputation: 123596
Was it not wrong,
int
would be altered as(*OP_TYPE)(int,int)
, and Isn't it right the second sentence become(*OP_TYPE)(int,int)(*p[4])(int,int) = {&Sum,&Sub,&Mul,&Div};
The declaration
typedef int (*OP_TYPE)(int, int);
does not change the meaning of int
- it changes the meaning of OP_TYPE
.
First, some background:
Declarations in C are broken into two main sections: a sequence of declaration specifiers, followed by a comma-separated list of declarators.
The declarator introduces the name of the thing being declared, along with information about that thing's array-ness, function-ness, and pointer-ness. In a typedef, that name becomes an alias for the type.
The structure of the declarator is meant to mirror the structure of an expression in the code. For example, suppose you have an array arr
of pointers to int
and you want to access the integer object pointed to by the i
’th element; you’d index into the array and dereference the result, like so:
printf( "%d\n", *arr[i] ); // *arr[i] is parsed as *(arr[i])
The type of the expression *arr[i]
is int
; that's why we write its declaration as
int *arr[N];
instead of
int *[N] arr;
In the expression, the operand of the postfix []
subscript operator is arr
, and the operand of the unary *
dereference operator is the expression arr[i]
. Those operators follow the same rules of precedence in a declaration, hence the structure of the declaration above.
The declaration reads as
arr -- arr
arr[N] -- is an array of
*arr[N] -- pointer to
int *arr[N]; -- int
Thus, object named arr
has type "array of pointer to int
";
Similarly, if you have an array of function pointers and you want to call one of those functions, you’d index into the array, dereference the result, and then call the resulting function with whatever arguments:
x = (*p[i])(a, b);
Again, the type of the expression (*p[i])(a, b)
is int
, so it follows that the declaration of p
is written
int (*p[4])(int, int);
and not
int (*[4])(int, int) p;
Again, the declaration reads as
p -- p
p[4] -- is a 4-element array of
*p[4] -- pointer to
(*p[4])( ) -- function taking
(*p[4])( , ) -- unnamed parameter
(*p[4])(int, ) -- is an int
(*p[4])(int, ) -- unnamed parameter
(*p[4])(int, int) -- is an int
int (*p[4])(int, int); -- returning int
so the object named p
has the type "array pointer to function taking two int
parameters and returning int
".
Clear so far?
So, how does typedef
affect this?
Let's start with a declaration without the typedef
:
int (*OP_TYPE)(int, int);
This declares OP_TYPE
as an object of type "pointer to function taking two int
parameters and returning int
". If we add the typedef
keyword:
typedef int (*OP_TYPE)(int, int);
it changes the meaning of the declaration such that OP_TYPE
is an alias for the type "pointer to function taking two int
parameters and returning int
". It doesn't change the structure of the declaration at all, only the meaning. Thus, you can write
OP_TYPE fp;
and it will mean exactly the same thing as
int (*fp)(int, int);
Some other examples may help drive the concept home; going back to the earlier examples, if
int *ap[N];
declares ap
as an object of type "4-element array of pointer to int
", then
typedef int *ap[N];
declares ap
as an alias for the type "4-element array of pointer to int
", such that
ap foo;
is equivalent to
int *foo[N];
If
int (*blah)[N];
declares blah
as an object of type "pointer to N-element array of int
", then the declaration
typedef int (*blah)[N];
declares blah
as an alias for the type "pointer to N-element array of int
".
In the example you provide,
unsigned short int US;
declares US
as object of type unsigned short int
; thus,
typedef unsigned short int US;
declares US
as an alias for the type unsigned short int
.
Declarations (and thus typedefs
) can get arbitrarily complex:
int *(*(*foo())[N])(double);
foo
is a pointer to a function that returns a pointer to an array of pointers to functions taking a double
parameter and returning a pointer to int
. Adding a typedef
:
typedef int *(*(*foo())[N])(double);
changes foo
to be an alias for the type "function returning a pointer to an array of pointers to functions taking a double
parameter and returning a pointer to int
; again,
foo bar;
is equivalent to writing
int *(*(*bar())[N])(double);
Upvotes: 1
Reputation: 17513
int
is not "altered" by a typedef
. typedef unsigned short int US;
is not "altering" unsigned short int
, it is defining the identifier US
to denote the same type as unsigned short int
. Likewise, typedef int (*OP_TYPE)(int,int)
is defining the identifier OP_TYPE
to denote the same type as int (*)(int, int)
(a pointer to a function returning int
with parameter type list int, int
).
Syntactically, typedef
is like a storage class specifier but it defines a "typedef name" instead of declaring a variable. Compare typedef int (*OP_TYPE)(int, int);
to static int (*op)(int, int);
. OP_TYPE
is a typedef name and op
is a variable. OP_TYPE
and op
both have the same type int (*)(int, int)
. OP_TYPE
can be used as a type in subsequent declarations, so that static OP_TYPE op;
is equivalent to static int (*op)(int, int);
Upvotes: 0
Reputation: 14167
The typedef
keyword behaves grammatically like storage modifiers (i.e. auto
, register
, static
etc) except the initialization part. My guess the reason for that is that the early versions of C compilers shared code between variable declaration and type alias declarations.
Therefore the type alias declaration and variable declaration look more or less the same:
static int (*A[4])(int,int); // staic variable
typedef int (*B[4])(int,int); // type alias
int (*C[4])(int,int); // local variable
I guess the question is why one cannot do:
int (*[4])(int,int) C;
I have no good answer to that, C grammar is simply defined this this way. I can guess that without the anchor the compiler cannot correctly parse the type.
So why one can't do:
(int (*[4])(int,int)) C;
Answer:
It would solve the problem with missing anchor. However, it cannot be used because it will conflict with cast operator (type) expr
. Note that symbol C
may be defined on out scope resulting in ambiguity.
int C;
{
// cast `C` to `int*` or maybe declare a pointer to `int`
(int*)C;
}
However, there is a workaround with typeof
extension (a feature in upcoming C23).
typeof(int (*[4])(int,int)) C;
This is more or less the same how the compiler expands the following declaration:
typedef int (*OP_TYPE)(int,int);
OP_TYPE p[4]={&Sum,&Sub,&Mul,&Div};
to
typeof(int(*)(int,int)) p[4] = { ... }
Moreover, using this trick allows a neat and readable declaration of complex types.
typeof(int(int,int))* p[4] = { .... }
It is easy to see that p
is a 4-element array of pointers to int(int,int)
function.
Upvotes: 3