views:

2057

answers:

4

I have a C application that I've created in VS2008. I am creating a mock creation function that overrides function references in a struct. However if I try and do this in a straight forward fashion with something like:

void *ptr = &(*env)->GetVersion;
*ptr = <address of new function>

then I get a "error C2100: illegal indirection" error as *ptr, when ptr is a void * seems to be a banned construct. I can get around it by using a int/long pointer as well, mapping that to the same address and modifying the contents of the long pointer:

*structOffsetPointer = &(*env)->GetVersion;
functionPointer = thisGetVersion;
structOffsetPointerAsLong = (long *)structOffsetPointer;
*structOffsetPointerAsLong = (long)functionPointer;

but I am concerned that using long or int pointers will cause problems if I switch between 32 and 64 bit environments.

So is there are easy way to disable this error? Assuming not, is either int or long 64 bits under win64?

+1  A: 

Last time I played with void* & C under visual studio, VS didn't play nicely. Here are some information datapoints:

A pointer is always the size of the system word(8/16/32/64)...(unless you have segmented memory, which I'm assuming you don't have). This is because it needs to point to anywhere in the memory space. For a von Neumann machine, a function pointer is going to be the same size as a data pointer, because data and code occupy the same memory space. This is not guaranteed under a Harvard architecture. I'm not familiar enough with Windows Vista to know if it programatically fakes out a Harvard architecture for security reasons.

I personally would not disable this error, just for the sake of letting the compiler do its job.

Paul Nathan
+1  A: 

As mentioned, to store the address of a function in a pointer you should simply not do the indirection.

However, you also talk about being worried about the size of an int type that you might store a pointer into (which generally is not something you want to do unless you have a really good reason to).

If you want to hold a pointer in an int type for some reason, then on Windows a UINT_PTR type (or uintptr_t from stdint.h if you have it) will hold most pointer types (I don't think it's necessarily large enough to hold some pointer-to-member types).

Michael Burr
So basically a uint will be 32 bits on a 32 bit OS and 64 bits on a 64 bit OS? If so, then that is the best type for me to use I think.
David Arno
Just to be clear - it's not `uint`. It's `UINT_PTR` (or `uintptr_t`). They are int types that are large enough to hold a pointer (so 32-bits on Win32 and 64-bits on Win64).
Michael Burr
+1  A: 

When dereferencing a "void *", you are left with a "void" which is has no size (or really no type for that matter), so it doesn't know how to assign something to it. It is the same as:

void blah = 0xdeadbabe; // let's assume a 32-bit addressing system

To add to my own response and give a solution, I would give it the proper type of a pointer to a function of the type GetVersion is. If GetVersion that your "env" struct field is pointing to is:

int GetVersion();

then you want:

int (**ptr)() = &(*env)->GetVersion;
Jim Buck
I had never really though of it like that before. I'd always viewed a void* as being a pointer to anything, rather than a pointer to nothing.
David Arno
I updated with an actual solution. Technically "void *" is a pointer to anything, but to dereference it is a snake eating its own tail. :)
Jim Buck
+1  A: 

Then how about:

void **ptr = (void **) &(*env)->GetVersion;
*ptr = <address of new function>

The right way to do this is to work with the type system, avoid all the casting and declare actual pointers to functions like:

typedef int (*fncPtr)(void);
fncPtr *ptr = &(*env)->GetVersion;
*ptr = NewFunction;

The above assumes GetVersion is of type fncPtr and NewFunction is declared as int NewFunction(void);

Tony Lee
That works just great (with a slight mod of void **ptr = (void **)). That tidies up the code very nicely. Thanks.
David Arno
I edit the code to match what worked for you
Tony Lee