Casting an address of subroutine into void pointer

2

Is it okay to cast function location with void pointer though function pointers size is not always the same as opaque pointer size?

I already did search about opaque pointers , and casting function pointers . I found out function pointers and normal pointers are not the same on some systems.

 void (*fptr)(void) = (void *) 0x00000009; // is that legal???

I feel I should do this

 void (*fptr)(void)= void(*)(void) 0x00000009;

It did work fine , though I expected some errors or at least warnings I'm using keil arm compiler

c
casting
embedded
function-pointers
void

3 Answers

2

No, the problem is that you cannot go between void* and function pointers, they are not compatible types. void* is the generic pointer type for object pointers only.

In the second case you have a minor syntax error, should be (void(*)(void)). Fixing that, we have:

void (*fptr)(void) = (void *) 0x00000009;         // NOT OK
void (*fptr)(void) = (void(*)(void)) 0x00000009;  // PERHAPS OK (but likely not on ARM)

Regarding the former, it is simply not valid C (but might be supported as a non-standard compiler extension). Specifically, it violates the rules of simple assignment, C17 6.5.16.1. Both operands of = must be compatible pointer types.

Regarding the latter, the relevant part is C17 6.3.2.3/5

An integer may be converted to any pointer type. Except as previously specified, the result is implementation-defined, might not be correctly aligned, might not point to an entity of the referenced type, and might be a trap representation.

Some special cases exist for null pointers, but that does not apply in your case.

So you can probably cast the value 9 to a function pointer - you have to check the Keil compiler manual regarding implementation-defined behavior. What meaningful purpose the resulting function pointer will have from there on, I have no idea.

Because the address 9 is certainly not an aligned address on ARM. Rather, it is 1 byte past where I'd expect to find the address of the non-maskable interrupt ISR or some such. So what are you actually trying to do here? Grab an address of a ISR from the vector table?

answered on Stack Overflow Aug 5, 2019 by Lundin • edited Aug 5, 2019 by Lundin
1
void (*fptr)(void) = (void *) 0x00000009;

is not legal according to standard C as such.

If the pointer on the left is integer constant expression with value 0, or such expression cast to (void *) is a null pointer constant, that can be assigned to a function pointer:

void (*fptr)(void) = 0;

This only applies to the null pointer constant. It does not even apply to a variable of type void * that contains a null pointer. The constraints (C11 6.5.16.1) for simple assignments include

  • the left operand is an atomic, qualified, or unqualified pointer, and the right is a null pointer constant; or

In strictest sense the standard does not provide a mechanism to convert a pointer-to-void to a pointer to function at all! Not even with a cast. However it is available on most common platforms as a documented common extension C11 J.5.7 Function pointer casts:

  1. A pointer to an object or to void may be cast to a pointer to a function, allowing data to be invoked as a function (6.5.4).

  2. A pointer to a function may be cast to a pointer to an object or to void, allowing a function to be inspected or modified (for example, by a debugger) (6.5.4).

but it is not required at all by the C standard - indeed it is possible to use C in a platform where the code can be executed only from memory that cannot be accessed as data at all.

answered on Stack Overflow Aug 5, 2019 by Antti Haapala • edited Jun 20, 2020 by Community
-1

The first expression is perfectly legal in C, as the void * pointer type is assignment and parameter passing compatible with any other pointer type, and you can run into trouble, if pointers are different size than integers. It's not good programming style, and there's apparently no reason to assign to a function pointer the integer literal 9. I cannot guess what are you doing so for.

Despite of that, there are some few (well, very few) cases in history that that thing has been done (e.g. to give the special values SIG_DFL and SIG_IGN to the signal(2) system call, one can assume nobody will ever use those values to call the function dereferenced by the pointer, indeed, you can use some integers, different than zero, in the page zero virtual addresses of a process, to avoid dereferencing the pointers (so you cannot call the functions, or you'll get a segmentation violation immediately), while using different than zero values to assume several values apart of the NULL pointer itself)

But the second expression is not legal. It's not valid for an expression to start with a type identifier, so the subexpression to the right of the = sign is invalid. To do a correct assignment, with a valid cast, you had to write:

void (*ptr)(void) = (void (*)(void)) 0x9; /* wth to write so many zeros? */

(enclosing the whole type mark in parenthesis) then, you can call the function as:

(*ptr)();

or simply as:

ptr();

Just writing

void(*ptr)(void) = 9;

is also legal, while the integer to pointer conversion is signalled by almost every compiler with a warning. You'll get an executable from there.

If the integer is 0, then the compiler will shut up, as 0 is converted automatically to the NULL pointer.

EDIT

To illustrate the simple use I mentioned above in the first paragraph, from the file <sys/signal.h> of FreeBSD 12.0:

File /usr/include/sys/signal.h

139 #define SIG_DFL         ((__sighandler_t *)0)
140 #define SIG_IGN         ((__sighandler_t *)1)
141 #define SIG_ERR         ((__sighandler_t *)-1)
142 /* #define  SIG_CATCH   ((__sighandler_t *)2) See signalvar.h */
143 #define SIG_HOLD        ((__sighandler_t *)3)

all those definitions are precisely of the type mentioned in the question, an integer value cast to a pointer to function, in order to permit special values to represent non executable/non callback values. The type __sighandler_t is defined as:

161 typedef void __sighandler_t(int);

below.

From CLANG:

$ cc -std=c17 -c pru.c
$ cat pru.c
void (*ptr)(void) = (void *)0x9;

you get even no warning at all.

Without the cast:

$ cc -std=c11 pru.c
pru.c:1:8: warning: incompatible integer to pointer conversion 
initializing 'void (*)(void)' with an expression of type 'int'
  [-Wint-conversion]
void (*ptr)(void) = 0x9;
       ^            ~~~
1 warning generated.

(Only a warning, not an error) With a zero literal:

$ cc -std=c11 -c pru.c
$ cat pru.c
void (*ptr)(void) = 0x0;

even no warning at all.

answered on Stack Overflow Aug 5, 2019 by Luis Colorado • edited Aug 6, 2019 by Luis Colorado

User contributions licensed under CC BY-SA 3.0