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

  • 1
    No, it is not legal, it is a constraint violation since you assign with a non-compatible pointer type as operand. – Lundin Aug 05 '19 at 11:05
  • 1
    At least, if you want to force `0x0000` to be an equivalent pointer, your cast should be: `void (*fptr)(void) = (void(*)(void)) 0x09;` (if you use `0`, then you are forced to use the cast, as `0` is compatible with all pointers in C) – Luis Colorado Aug 05 '19 at 18:38

3 Answers3

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?

Lundin
  • 195,001
  • 40
  • 254
  • 396
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.

Community
  • 1
  • 1
  • 1
    `0x00000009` is an integer constant with the value 9, did you miss the 9 at the end? It is not a null pointer constant. – Lundin Aug 05 '19 at 11:03
  • A null pointer, not just a null pointer constant, may be converted to another pointer type, and it yields a null pointer of that type: C 2018 6.3.2.3 4: “Conversion of a null pointer to another pointer type yields a null pointer of that type.” Note that this paragraph does not restrict the conversion to a “pointer to any object type” as some other paragraphs in this clause do. – Eric Postpischil Aug 05 '19 at 11:10
  • @EricPostpischil yes, but it requires an explict cast **and** in case of function pointers need not be compilable at all. – Antti Haapala -- Слава Україні Aug 05 '19 at 11:15
  • @Lundin ah I did miss I guess :D – Antti Haapala -- Слава Україні Aug 05 '19 at 11:15
  • @Lundin I blame my 4k display *and* dotted 0 in my code font. :D – Antti Haapala -- Слава Україні Aug 05 '19 at 11:16
  • @AnttiHaapala: C 2018 6.3.2.3 4 means the statement “In strictest sense the standard does not provide a mechanism to convert a pointer-to-void to a pointer to function at all!” is false. – Eric Postpischil Aug 05 '19 at 11:18
  • @EricPostpischil because the conversion of an object pointer to function pointer is not allowed, null or not. – Antti Haapala -- Слава Україні Aug 05 '19 at 11:19
  • @AnttiHaapala: 6.3.2.3 4 explicitly specifies the conversion. Nothing else disallows it. – Eric Postpischil Aug 05 '19 at 11:20
  • @EricPostpischil you're reading it incorrectly. The behaviour of a conversion *were* it to happen is that the result is a null pointer of that type. But the conversion of a data pointer type to a function pointer is undefined behaviour by omission. – Antti Haapala -- Слава Україні Aug 05 '19 at 11:25
  • @AnttiHaapala: That paragraph defines the behavior of converting a null pointer in a data pointer type or any other pointer type to a function pointer type or any other pointer type. It is not undefined by omission because it is not omitted: It is in paragraph 4. That paragraph explicitly defines the behavior of converting a null pointer to another pointer type. The fact that neither it, nor any other paragraph, defines the behavior of converting any pointer value, not just null, to another pointer type, does not negate the fact that this paragraph defines the conversion of a null pointer. – Eric Postpischil Aug 05 '19 at 11:27
-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.

Luis Colorado
  • 10,974
  • 1
  • 16
  • 31
  • "The first expression is perfectly legal, as the void * pointer type is assignment and parameter passing compatible with any other pointer type." No, only to object pointer types. See 6.3.2.3. – Lundin Aug 05 '19 at 18:51
  • Ok `void(*ptr)(void) = 9;` is just plain wrong and not legal at all. Please read [“Pointer from integer/integer from pointer without a cast” issues](https://stackoverflow.com/questions/52186834/pointer-from-integer-integer-from-pointer-without-a-cast-issues). – Lundin Aug 05 '19 at 18:54
  • The link I provided states the error message given by 4 mainstream compilers. – Lundin Aug 05 '19 at 18:55
  • Wasn't it legal mean a zillion of lines of code and working to be incorrect... Being deprecated or not recommended, doesn't mean illegal. C has had since the beginning to be able to parse things like `int main(argc, argv) char**argv; {}` as correct (because they are correct C code) and, while deprecated, it is valid C code, as with `if (3["0123"] == '3') then printf("You win!!!\n");` – Luis Colorado Aug 05 '19 at 18:58
  • Please note that the C standard does not mention things like warnings and errors, it just speaks of diagnostic messages. Meaning that code that "just" gives a warning may be invalid C, as it is in the case of `void(*ptr)(void) = 9;`. It is not deprecated because it has never been valid C. Some broken, ancient compilers failed to give a diagnostic message for such code, because they were non-conforming. – Lundin Aug 05 '19 at 18:59
  • yeah, what you want to say.... one thing is what the standard says, and other well different what is C code (without any 99, 11 or else tag) I will not begin another standards war of discussions about what does your document say. Do you remember when the auto increment operators where written as `=+`, `=-`, etc? No, well I grew with those... and believe me, the UNIX system was written in C. – Luis Colorado Aug 05 '19 at 19:02
  • I don't even know what you are talking about, but the rules of simple assignment have been the same since pre-standard K&R. This hasn't changed for over 40 years. – Lundin Aug 05 '19 at 19:04
  • The rules you are using are published 8-9 years ago... but then, C was 40 years old already.... There's no need to remoutn to 1970's C to get that pointer cast as valid.... just before the 2011 standard it was.... I'm not going to discuss if it is standard compliant or not... but it is C, of course it is!!! – Luis Colorado Aug 05 '19 at 19:09
  • See [pre C89 draft](http://port70.net/~nsz/c/c89/c89-draft.html#3.3.16.1) 3.3.16.1 "both operands are pointers to qualified or unqualified versions of compatible types". This is from bloody 1988. As I already told you, this has never been valid C and simple assignment has barely changed over the years. Something isn't valid C just because you say so. – Lundin Aug 05 '19 at 19:19
  • Computer archaeology from 1978, K&R 1st edition, p192: "The compilers currently allow a pointer to be assigned to an integer, an integer to a pointer, and a pointer to a pointer of another type. /--/ This usage is nonportable and may produce pointers which cause addressing exceptions when used." – Lundin Aug 05 '19 at 19:29
  • The same non-portability that predates the question. Or do you agree with me that `void (*ptr)(void) = (void (*ptr)(void)) 0x9;` is a normal (and good) use/habit of the C (and it's C syntax) language? Do I have to accept the question, accept your mistake in your cast, and then accept that my proposal is incorrect because I'm using old legacy customs? (and of course your downvote, I didn't downvote you when I told you about your mistake, BTW) I'm not telling if that's is good or bad programming style, I'm just talking about legal C code. – Luis Colorado Aug 06 '19 at 13:37
  • @Lundin, please, read my last edit to my answer to see how the question can be used for something useful, and is done indeed in the UNIX system today (not in K&R archaeology) Do you actually feel well talking as you do about Brian Kernighan and Dennis Ritchie? Don't you feel that they deserve some respect from the new generations? The superfluous usage can lead to nonportability, but as UNIX has demonstrated, it can also be ported to many architectures. – Luis Colorado Aug 06 '19 at 13:52
  • The code you posted now has an explicit cast which is fine, unlike `void(*ptr)(void) = 9;`, as proven by the numerous sources already given. – Lundin Aug 06 '19 at 14:11
  • I recommend using the cast, reread my answer, or whatever version you want, you'll see that I don't say ever _use that_ I say that the cast to `(void *)` (which is also a pointer) is perfectly legal. I say, later, that the avoidance of the cast is also legal, while most probably a programming error (not ilegal C construct) use of the pointer (not being the integer the literal `0`, in which case it is legal even in `C++`. (as stated in Stroustrup 4Ed. at least) – Luis Colorado Aug 06 '19 at 14:14
  • But **it is not legal C and has never been**, as already proven by quoting C11, C89, pre-C89 draft and K&R 1st edition. I'm done talking to you. – Lundin Aug 06 '19 at 14:16
  • Nope, it's a programming error, most probably. Assigning an integer (different than zero) to a pointer value is an error **only** in C11, but never before. Just compile it with C98 and you'll see. Assigning `0` to a pointer value is and has been valid always (an `0` is an integer value) – Luis Colorado Aug 06 '19 at 14:18
  • No, you are just rambling, it has not changed from pre C89 to C17, which I have already proven. Study the sources I have given you. Or indeed try it with C90, C99 or whatever version you fancy. https://godbolt.org/z/uiEXGW. It is a constraint violation of the C language and has always been so. – Lundin Aug 06 '19 at 14:23