De-referencing pointer to function-pointer

E

Edd

Hello all,

I've made a data structure and an associated set of functions to enable me
to store a dynamically-sized array of elements of whatever data type I like.
Well that's the idea anyway...
My implementation seems to work great for primitive types, structures and
unions, but I can't quite get an array of function-pointers working
properly. Here's a very reduced version of my code with error checks removed
for the sake of brevity.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

#define ALLOC_FACTOR 10

/* Dynamically-allocated array structure */
typedef struct TYPE_ARRAY {
void *base;
size_t elsz;
unsigned block, len;
}
ARRAY;

/* Initialise an array to an 'empty' state */
int InitArray(ARRAY *a, size_t elsz){
a->base = NULL;
a->elsz = elsz;
a->block = a->len = 0;
return 0;
}

/* Add an element to an array */
int AddElement(ARRAY *a, void *el){
/* Allocate some space if necessary */
if(a->block == a->len){
a->base = realloc(a->base, a->elsz*(a->block + ALLOC_FACTOR));
a->block += ALLOC_FACTOR;
}
/* Copy a new element on to the end of the array */
memmove((char *)(a->base) + (a->elsz*a->len++), el, a->elsz);
return 0;
}

/* Get the address of the kth element */
void *GetElement(ARRAY *a, unsigned k){
return (char *)(a->base) + (k * a->elsz);
}


int main(void){
double (*f)(double);
ARRAY funcs;
InitArray(&funcs, sizeof(sin));

/* Add some functions to the funcs ARRAY */
AddElement(&funcs, &sin);
AddElement(&funcs, &tan);
AddElement(&funcs, &exp);
AddElement(&funcs, &log);

/* Get the ARRAY element at index 2 */
f = *(double (**)(double))GetElement(&funcs, 2);

/* This should now display "f(1.0) = 2.718..."? */
printf("f(%lf) = %lf\n", 1.0, f(1.0));

return 0;
}


This program crashes when I run it. Am I doing something undefined here? I
can't see what's going wrong. I think it may be my understanding of
function-pointer syntax is a little lacking, but what I've got still seems
fine to me.
Here's an alternative main() function which makes a dynamic array of doubles
instead of function pointers. This one seems to work fine:

int main(void){
double src[4] = {0.0, 1.0, 3.14, 2.718};
ARRAY dbls;
InitArray(&dbls, sizeof(double));
double d;

/* Add some doubles to the dbls ARRAY */
AddElement(&dbls, src);
AddElement(&dbls, src+1);
AddElement(&dbls, src+2);
AddElement(&dbls, src+3);

/* Get the ARRAY element at index 2 */
d = *(double *)GetElement(&dbls, 2);

/* This should now display "d = 3.14" */
printf("d = %lf\n", d);

return 0;
}


Can anyone point me in the right direction?
Thanks in advance,

Edd
 
J

Jack Klein

Hello all,

I've made a data structure and an associated set of functions to enable me
to store a dynamically-sized array of elements of whatever data type I like.
Well that's the idea anyway...
My implementation seems to work great for primitive types, structures and
unions, but I can't quite get an array of function-pointers working
properly. Here's a very reduced version of my code with error checks removed
for the sake of brevity.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

#define ALLOC_FACTOR 10

/* Dynamically-allocated array structure */
typedef struct TYPE_ARRAY {
void *base;
size_t elsz;
unsigned block, len;
}
ARRAY;

/* Initialise an array to an 'empty' state */
int InitArray(ARRAY *a, size_t elsz){
a->base = NULL;
a->elsz = elsz;
a->block = a->len = 0;
return 0;
}

/* Add an element to an array */
int AddElement(ARRAY *a, void *el){
/* Allocate some space if necessary */
if(a->block == a->len){
a->base = realloc(a->base, a->elsz*(a->block + ALLOC_FACTOR));
a->block += ALLOC_FACTOR;
}
/* Copy a new element on to the end of the array */
memmove((char *)(a->base) + (a->elsz*a->len++), el, a->elsz);
return 0;
}

/* Get the address of the kth element */
void *GetElement(ARRAY *a, unsigned k){
return (char *)(a->base) + (k * a->elsz);
}


int main(void){
double (*f)(double);
ARRAY funcs;
InitArray(&funcs, sizeof(sin));

Either your compiler is broken or you are not invoking it as a C
compiler and making use of some implementation-defined extension. It
is a constraint violation to apply the sizeof operator to a function
designator, and requires a diagnostic.

The expression "sizeof(sin)" has literally no meaning in C. I have no
idea what value your compiler generates when you apply sizeof to a
function. Do you?

[snip]
This program crashes when I run it. Am I doing something undefined here? I
can't see what's going wrong. I think it may be my understanding of
function-pointer syntax is a little lacking, but what I've got still seems
fine to me.

Yes, you are doing something undefined here. Function and array names
are not converted to pointers when used as operands of the sizeof
operator.

sizeof(array_name) yields the size, in bytes, of an array, not of a
pointer to the element type of the array.

sizeof(function_name) would request the compiler to yield the size, in
bytes, of the function, not of a pointer to the function. But
functions have no sizes accessible to a C program, and that use of
sizeof is specifically illegal under the C standard.
 
S

Sameer

AddElement(&funcs, &sin) is copying the first sizeof(double
(*)(double)) bytes of instruction code of sin function, and not its
address.

Use,

double (*fptrs[]) (double) = {sin, tan, exp, log};

AddElement(&funcs, fptrs);
AddElement(&funcs, fptrs + 1);
AddElement(&funcs, fptrs + 2);
AddElement(&funcs, fptrs + 3);

HTH.

-Sameer
 
E

Edd

[ 8< - - - snip ]
int main(void){
double (*f)(double);
ARRAY funcs;
InitArray(&funcs, sizeof(sin));

Either your compiler is broken or you are not invoking it as a C
compiler and making use of some implementation-defined extension. It
is a constraint violation to apply the sizeof operator to a function
designator, and requires a diagnostic.

The expression "sizeof(sin)" has literally no meaning in C. I have no
idea what value your compiler generates when you apply sizeof to a
function. Do you?

[snip]

I see. Indeed I don't understand what my compiler generates under these
circumstances! However my compiler does not complain about this code in the
slightest, even when I turn on all warnings and support strict ANSI C. I'm
using MinGW under win2k with this command line:

gcc -Wall -ansi ptrtest.c -o ptrtest.exe

I just tried this on my University system with gcc on unix and I got errors.
Is this a problem with my home compiler, do you think -- should it warn me?

Yes, you are doing something undefined here. Function and array names
are not converted to pointers when used as operands of the sizeof
operator.

sizeof(array_name) yields the size, in bytes, of an array, not of a
pointer to the element type of the array.

sizeof(function_name) would request the compiler to yield the size, in
bytes, of the function, not of a pointer to the function. But
functions have no sizes accessible to a C program, and that use of
sizeof is specifically illegal under the C standard.

I see. Thanks for the clarification.
This leads me on to the obvious follow-up question -- is there a way of
achieving the desired result? I can use the alternative method below (which
works correctly), but it's not quite as elegant as I would like:

int main(void){
double (*f)(double);
void *vptr;
ARRAY funcs;
InitArray(&funcs, sizeof(void*));

/* Add some functions to the funcs ARRAY */
vptr = sin;
AddElement(&funcs, &vptr);
vptr = tan;
AddElement(&funcs, &vptr);
vptr = exp;
AddElement(&funcs, &vptr);
vptr = log;
AddElement(&funcs, &vptr);

/* Get the ARRAY element at index 2 */
f = (double (*)(double))*(void**)GetElement(&funcs, 2);

/* This should now display "f(1.0) = 2.718..."? */
printf("f(%lf) = %lf\n", 1.0, f(1.0));

return 0;
}

Thanks for you reply,
Edd
 
J

Jack Klein

[ 8< - - - snip ]
int main(void){
double (*f)(double);
ARRAY funcs;
InitArray(&funcs, sizeof(sin));

Either your compiler is broken or you are not invoking it as a C
compiler and making use of some implementation-defined extension. It
is a constraint violation to apply the sizeof operator to a function
designator, and requires a diagnostic.

The expression "sizeof(sin)" has literally no meaning in C. I have no
idea what value your compiler generates when you apply sizeof to a
function. Do you?

[snip]

I see. Indeed I don't understand what my compiler generates under these
circumstances! However my compiler does not complain about this code in the
slightest, even when I turn on all warnings and support strict ANSI C. I'm
using MinGW under win2k with this command line:

gcc -Wall -ansi ptrtest.c -o ptrtest.exe

I just tried this on my University system with gcc on unix and I got errors.
Is this a problem with my home compiler, do you think -- should it warn me?

Not just should, but it required to. When a source program violates
syntax or a constraint, the C standard requires the compiler to issue
a diagnostic, although it does not specify the format of the
diagnostic.

I haven't used GCC ports much, nor recently, but I think you might
need to add -pedantic.
I see. Thanks for the clarification.
This leads me on to the obvious follow-up question -- is there a way of
achieving the desired result? I can use the alternative method below (which
works correctly), but it's not quite as elegant as I would like:

int main(void){
double (*f)(double);
void *vptr;
ARRAY funcs;
InitArray(&funcs, sizeof(void*));

No, no, no, no. There is no correspondence between pointers to object
types and pointers to functions in C. Even attempting to convert
between a function pointer and a pointer to void, in either direction,
is completely undefined.

Fortunately, you don't need to. You already have a perfect operand
here, just replace the line above with:

InitArray(&funcs, sizeof f);

f is already a pointer to function, not the name of a function, so
applying sizeof to it is just fine and dandy. Also, since f is an
object and not a type, the parentheses are not necessary, but harmless
if you prefer them.
/* Add some functions to the funcs ARRAY */
vptr = sin;
AddElement(&funcs, &vptr);
vptr = tan;
AddElement(&funcs, &vptr);
vptr = exp;
AddElement(&funcs, &vptr);
vptr = log;
AddElement(&funcs, &vptr);

Leave out vptr completely, just omit if from your program. Now that
you have uses sizeof f to initialize your structure, you can just do:

AddElement(&funcs, sin);
AddElement(&funcs, tan);

....and so on.

No problem with using the name of a function without () as an argument
passed to another function. Unlike with the sizeof operator, this is
well defined and automatically converts the name of the function to a
pointer to the function.
/* Get the ARRAY element at index 2 */
f = (double (*)(double))*(void**)GetElement(&funcs, 2);

/* This should now display "f(1.0) = 2.718..."? */
printf("f(%lf) = %lf\n", 1.0, f(1.0));

return 0;
}

Thanks for you reply,
Edd

I have copied this from your original post:
/* Get the address of the kth element */
void *GetElement(ARRAY *a, unsigned k){
return (char *)(a->base) + (k * a->elsz);
}

The first thing I would do is change the return type to "const void
*", but that's not mandatory.

To retrieve a function pointer from your array, you can get rid of all
that almost indecipherable casting by doing this:

void *vp;
vp = GetElement(&funct, 2);
memcpy(&f, vp, sizeof f);

The latter two lines can be combined, with rather less readability, to
eliminate the need for the pointer to void:

memcpy(&f, GetElement(&func, 2), sizeof f);

All the nasty casts are gone!
 
E

Edd

Jack Klein wrote:

[ 8< - - - snip ]
No, no, no, no. There is no correspondence between pointers to object
types and pointers to functions in C. Even attempting to convert
between a function pointer and a pointer to void, in either direction,
is completely undefined.

Apologies for that. I keep meaning to get a copy of the standard, but I'm a
bit strapped for cash at the moment. I'll have one soonish though, with any
luck. Should save on your mental anguish... :)
Fortunately, you don't need to. You already have a perfect operand
here, just replace the line above with:

InitArray(&funcs, sizeof f);

f is already a pointer to function, not the name of a function, so
applying sizeof to it is just fine and dandy. Also, since f is an
object and not a type, the parentheses are not necessary, but harmless
if you prefer them.


Leave out vptr completely, just omit if from your program. Now that
you have uses sizeof f to initialize your structure, you can just do:

AddElement(&funcs, sin);
AddElement(&funcs, tan);

...and so on.

No problem with using the name of a function without () as an argument
passed to another function. Unlike with the sizeof operator, this is
well defined and automatically converts the name of the function to a
pointer to the function.

[ 8< - - - snip ]
To retrieve a function pointer from your array, you can get rid of all
that almost indecipherable casting by doing this:

void *vp;
vp = GetElement(&funct, 2);
memcpy(&f, vp, sizeof f);

The latter two lines can be combined, with rather less readability, to
eliminate the need for the pointer to void:

memcpy(&f, GetElement(&func, 2), sizeof f);

All the nasty casts are gone!

That's all splendid, Jack, thanks very much!

Edd
 
B

Ben Pfaff

Jack Klein said:
The expression "sizeof(sin)" has literally no meaning in C. I have no
idea what value your compiler generates when you apply sizeof to a
function. Do you?

He is the victim of an irritating GNU C extension. From the GNU
C compiler manual:

Arithmetic on `void'- and Function-Pointers
===========================================

In GNU C, addition and subtraction operations are supported
on pointers to `void' and on pointers to functions. This is
done by treating the size of a `void' or of a function as 1.
 
E

Edd

Jack Klein wrote:

[ 8< - - - snip ]

Sorry to bother you again!...
I have copied this from your original post:


The first thing I would do is change the return type to "const void
*", but that's not mandatory.

By a similar token, I guess I should have "const ARRAY *a" as the first
function argument, too (and similar things apply to some of the pointers
passed to my other functions).
To retrieve a function pointer from your array, you can get rid of all
that almost indecipherable casting by doing this:

void *vp;
vp = GetElement(&funct, 2);
memcpy(&f, vp, sizeof f);

The latter two lines can be combined, with rather less readability, to
eliminate the need for the pointer to void:

memcpy(&f, GetElement(&func, 2), sizeof f);

I just tried what you suggested, but got a number of compilation errors of
this kind:

amos $ gcc -Wall -ansi -pedantic ptrfunc.c -o ptrfunc -lm
ptrfunc.c: In function `main':
ptrfunc.c:48: warning: ANSI forbids passing arg 2 of `AddElement' between
function pointer and `void *'
[ 8< - - - remaining output snipped ]

A wise man once said:
"No, no, no, no. There is no correspondence between pointers to object
types and pointers to functions in C. Even attempting to convert between a
function pointer and a pointer to void, in either direction, is completely
undefined."

For completeness, here's the latest main() function:

int main(void){
double (*f)(double);
ARRAY funcs;
InitArray(&funcs, sizeof(f));

/* Add some functions to the funcs ARRAY */
AddElement(&funcs, sin);
AddElement(&funcs, tan);
AddElement(&funcs, exp);
AddElement(&funcs, log);

/* Get the ARRAY element at index 2 */
memcpy(&f, GetElement(&funcs, 2), sizeof(f));

/* This should now display "f(1.0) = 2.718..."? */
printf("f(%lf) = %lf\n", 1.0, f(1.0));

return 0;
}

Is there any way to fix this? I assume that this means I can't use the same
function (AddElement) to add both primitive data and function pointers to an
ARRAY?

Also, I'm still a little confused about what (e.g.) this does:
memmove(target, sin, a->elsz);
which is effectively done in the call to AddElement(&funcs, sin).
Is it copying the first sizeof(f) bytes of the compiled code for the sin
function into the array block, or is it copying the address of sin (whatever
that means)? Should I not be doing AddElement(&funcs, &sin), anyway? As I
mentioned before, my knowledge of functions pointers in this respect is a
bit lacking! Any clarification would be greatly appreciated!

Thanks,
Edd
 
T

those who know me have no need of my name

[slightly reordered]

in comp.lang.c i read:
ptrfunc.c: In function `main':
ptrfunc.c:48: warning: ANSI forbids passing arg 2 of `AddElement' between
function pointer and `void *'
Is there any way to fix this?

not portably, as you can see from gcc's warning. it'll work with some
compilers, but it will fail elsewhere, and might fail even with a different
version of the same compiler or with just different options selected.
I assume that this means I can't use the same function (AddElement) to add
both primitive data and function pointers to an ARRAY?

correct, you cannot do both in a portable / strictly conforming program.
/* Get the ARRAY element at index 2 */
memcpy(&f, GetElement(&funcs, 2), sizeof(f));

i have no idea why someone would bother with this bit of undefined behavior
instead of the much clearer but still undefined yet likely to have the same
result:

typedef double (*fptr)(double);
fptr f = (fptr)GetElement(&funcs, 2);
Should I not be doing AddElement(&funcs, &sin), anyway?

a function's name without the () postfix operator yields a pointer to the
function, so there's no need to use the & prefix operator.
my knowledge of functions pointers in this respect is a
bit lacking! Any clarification would be greatly appreciated!

<http://www.function-pointer.org>
 
E

Edd

those who know me have no need of my name wrote:

[ 8< - - - snip ]
correct, you cannot do both in a portable / strictly conforming
program.

Ok, I might as well give up on this idea for now, or at least find a
longer-winded way to do things, tailored to functions.
i have no idea why someone would bother with this bit of undefined
behavior instead of the much clearer but still undefined yet likely
to have the same result:

typedef double (*fptr)(double);
fptr f = (fptr)GetElement(&funcs, 2);

Even if AddElement did exactly what it should do intuitively (which, as
we've discussed, is pretty much impossible to achieve in a portable way), it
would mean that this call to GetElement would return the /address/ of a
function-pointer and not a function-pointer 'disguised' as (void*). So
casting it to type fptr is no good. If anything, it would have to be casted
to a double (**)(double) and de-referenced.

It's all pointless anyway. Wreaks of un-definedness as you and Jack have
indicated, so I'll leave it alone!

[ 8< - - - snip ]

I'll have a look at that! Thanks for your help,

Edd
 
M

Michael Wojcik

a function's name without the () postfix operator yields a pointer to the
function, so there's no need to use the & prefix operator.

Except when it's the operand of the & operator (so you don't get a
pointer to a pointer to function), or of the sizeof operator, which
is why

sizeof sin

is not simply equivalent to

sizeof &sin

or

sizeof(double (*)(double))

I notice n869 has a footnote (#47) specifically noting this sizeof
exception, but it doesn't say why. (Maybe the committee felt that
people would assume that "sizeof function" would give them the size
of the code generated for function, which they might then try to use
for some horrible and unportable purpose.) Must get a copy of the
Rationale one of these days...
 
A

Arthur J. O'Dwyer

Except when it's the operand of the & operator (so you don't get a
pointer to a pointer to function), or of the sizeof operator, which
is why
sizeof sin
is not simply equivalent to
sizeof &sin
I notice n869 has a footnote (#47) specifically noting this sizeof
exception, but it doesn't say why.

Symmetry and consistency between the handling of arrays and the
handling of functions. Both "decay" under the same circumstances.
Since 'sizeof array' gives the size of the *array*, not the size of
the decayed pointer, 'sizeof function' should not give the size of
the decayed pointer either. However, "the size of the function"
doesn't make any sense w.r.t. C's abstract semantics; thus, 'sizeof
function' is disallowed.
(Maybe the committee felt that
people would assume that "sizeof function" would give them the size
of the code generated for function, which they might then try to use
for some horrible and unportable purpose.)

Letting the user get the "size of" a function is useless, since
you can't do arithmetic on function pointers, nor read or store
through them. So if the Standard were to allow this construct, its
semantics would basically be, "sizeof f, where f is a function,
shall return a number. There's nothing you can do with this number,
but it's supposed to be bigger for more complicated functions, in
general." That's silly.
Implementations are perfectly free to define 'sizeof f' themselves
to be whatever they want, AFAIK.

-Arthur
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,581
Members
45,056
Latest member
GlycogenSupporthealth

Latest Threads

Top