Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Conversion from void* to pointer-to-function?

1,758 views
Skip to first unread message

Keith Thompson

unread,
Jun 25, 2012, 4:26:25 PM6/25/12
to
Consider the following toy program:

int main(void) {
void *void_ptr = 0;
void (*func_ptr)(void) = (void(*)(void))void_ptr;
return 0;
}

(Note that void_ptr is *not* a null pointer constant, it's merely a
pointer object that happens to contain a null pointer.)

Is the conversion of void_ptr to void(*)(void) (a pointer-to-function
type) permitted?

N1570 6.5.4 lists three constraints for the cast operator. This code
doesn't violate any of them.

N1570 6.3.2.3 describes the semantics of conversions involving pointers.
It mentions conversions from void* to and from pointer-to-object types,
conversions from one pointer-to-object type to another, and conversions
from one pointer-to-function type to another. Conversions from void* to
a pointer-to-function type simply aren't mentioned.

My conclusion is that the conversion is permitted (it doesn't violate
any constraint or syntax rule), but its behavior is undefined by
omission.

Note that the authors of gcc seem to have a different interpretation;
on the above code, gcc-4.7 with "-std=c99 -pedantic" produces the
following warning:

warning: ISO C forbids conversion of object pointer to function pointer type [-pedantic]

Frankly I think it would make more sense if the conversion *were*
a constraint violation, or else if its behavior were explicitly
stated to be undefined (or the result to be implementation-defined
or unspecified).

--
Keith Thompson (The_Other_Keith) ks...@mib.org <http://www.ghoti.net/~kst>
Will write code for food.
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"

James Kuyper

unread,
Jun 25, 2012, 5:23:48 PM6/25/12
to
On 06/25/2012 04:26 PM, Keith Thompson wrote:
> Consider the following toy program:
>
> int main(void) {
> void *void_ptr = 0;
> void (*func_ptr)(void) = (void(*)(void))void_ptr;
> return 0;
> }
>
> (Note that void_ptr is *not* a null pointer constant, it's merely a
> pointer object that happens to contain a null pointer.)
>
> Is the conversion of void_ptr to void(*)(void) (a pointer-to-function
> type) permitted?
>
> N1570 6.5.4 lists three constraints for the cast operator. This code
> doesn't violate any of them.
>
> N1570 6.3.2.3 describes the semantics of conversions involving pointers.
> It mentions conversions from void* to and from pointer-to-object types,
> conversions from one pointer-to-object type to another, and conversions
> from one pointer-to-function type to another. Conversions from void* to
> a pointer-to-function type simply aren't mentioned.

Conversion of null pointers to other pointer types is mentioned
(6.3.6.3p4). It applies only to null pointers being converted to pointer
types, but there are no other type-based restrictions on the
applicability of that section.

> My conclusion is that the conversion is permitted (it doesn't violate
> any constraint or syntax rule), but its behavior is undefined by
> omission.

The behavior is defined by 6.3.6.3p4: it produces a null pointer of type
void(*)(void).

Keith Thompson

unread,
Jun 25, 2012, 5:57:34 PM6/25/12
to
It's 6.3.2.3p4, not 6.3.6.3p4.

You're right, of course -- which makes me realize I didn't ask what I
intended to ask. I meant to ask about conversion from void* to a
pointer-to-function type in general, not about conversion of null
pointers.

A revised test case:

#include <stdlib.h>
#include <assert.h>
int main(void) {
void *void_ptr = malloc(1024);
assert(void_ptr != NULL);
void (*func_ptr)(void) = (void(*)(void))void_ptr;
return 0;
}

Assume that the assert doesn't fire.

I believe that the conversion in the initializer for void_ptr is
legal (i.e., doesn't violate any syntax rule or constraint) but has
undefined behavior by omission. (The authors of gcc, based on the
warning given with "gcc -std=c99 -pedantic", apparently disagree.)

Is my interpretation correct?

I know that the standard explicitly allows for undefined behavior when
the behavior is quietly not specified, but IMHO the standard would be
improved if 6.3.2.3 covered this case and explicitly stated that the
behavior is undefined.

Maxim Fomin

unread,
Jun 27, 2012, 1:03:41 PM6/27/12
to
вторник, 26 июня 2012 г., 1:57:34 UTC+4 пользователь Keith Thompson написал:
I think there is another interpretation of this issue.

6.2.5 p.20: "A pointer type may be derived from a function type or an object type, called the referenced type".
"referenced type" is highlighted and supposed to be important.

6.3.2.1 p.8: "If a converted pointer is used to call a function whose type is not compatible with the referenced type, the behavior is undefined."

So, this statement should be applied to both object and function pointers and not only to function pointers. 6.2.5 states that there are two groups of pointers: pointers-to-object and pointers-to function. It doesn't state that there is a third case of void* pointer, so void* should be pointer-to-object because its type is incomplete (object types may be incomplete) and it may be freely converted to any pointer to object.

Keith Thompson

unread,
Jun 27, 2012, 2:33:23 PM6/27/12
to
Maxim Fomin <ma...@maxim-fomin.ru> writes:
[...]
>> A revised test case:
>>
>> #include <stdlib.h>
>> #include <assert.h>
>> int main(void) {
>> void *void_ptr = malloc(1024);
>> assert(void_ptr != NULL);
>> void (*func_ptr)(void) = (void(*)(void))void_ptr;
>> return 0;
>> }
>>
>> Assume that the assert doesn't fire.
>>
>> I believe that the conversion in the initializer for void_ptr is
>> legal (i.e., doesn't violate any syntax rule or constraint) but has
>> undefined behavior by omission. (The authors of gcc, based on the
>> warning given with "gcc -std=c99 -pedantic", apparently disagree.)
>>
>> Is my interpretation correct?
>>
>> I know that the standard explicitly allows for undefined behavior when
>> the behavior is quietly not specified, but IMHO the standard would be
>> improved if 6.3.2.3 covered this case and explicitly stated that the
>> behavior is undefined.
>
> I think there is another interpretation of this issue.
>
> 6.2.5 p.20: "A pointer type may be derived from a function type or an
> object type, called the referenced type". "referenced type" is
> highlighted and supposed to be important.

"referenced type" is in italics, which simply means that this is the
definition of the term (see 3p1).

> 6.3.2.1 p.8: "If a converted pointer is used to call a function whose
> type is not compatible with the referenced type, the behavior is
> undefined."

That's not relevant to this case. It refers to calling a function with a
function pointer of the wrong type. The prefix of a function call is an
expression of pointer-to-function type; see 6.5.2.2p1. If the prefix is
a pointer to an object type, that's a constraint violation.

> So, this statement should be applied to both object and function
> pointers and not only to function pointers. 6.2.5 states that there
> are two groups of pointers: pointers-to-object and pointers-to
> function. It doesn't state that there is a third case of void*
> pointer, so void* should be pointer-to-object because its type is
> incomplete (object types may be incomplete) and it may be freely
> converted to any pointer to object.

In C99, types are partitioned into object types, function types, and
incomplete types (void is an incompletetype).

C11 changes this; now types are partitioned into function typed and
object types. An object type may be incomplete or complete. (6.2.5p1).

Certainly void* may be converted to any pointer to object. My question
is about converting void* to a pointer to function.

David R Tribble

unread,
Jul 24, 2012, 11:05:34 PM7/24/12
to
On Wednesday, June 27, 2012 1:33:23 PM UTC-5, Keith Thompson wrote:
> Certainly void* may be converted to any pointer to object. My question
> is about converting void* to a pointer to function.

Well, it's certainly going to invoke undefined (or at least
implementation-defined) behavior on architectures having
sizeof(void (*)()) != sizeof(void *), i.e., function
pointers of different (usually wider) sizes.

-drt

Tim Rentsch

unread,
Dec 21, 2012, 3:32:48 AM12/21/12
to
Keith Thompson <ks...@mib.org> writes:

> Consider the following toy program:
>
> int main(void) {
> void *void_ptr = 0;
> void (*func_ptr)(void) = (void(*)(void))void_ptr;
> return 0;
> }
>
> (Note that void_ptr is *not* a null pointer constant, it's merely a
> pointer object that happens to contain a null pointer.)
>
> Is the conversion of void_ptr to void(*)(void) (a pointer-to-function
> type) permitted?
>
> N1570 6.5.4 lists three constraints for the cast operator. This code
> doesn't violate any of them.
>
> N1570 6.3.2.3 describes the semantics of conversions involving pointers.
> It mentions conversions from void* to and from pointer-to-object types,
> conversions from one pointer-to-object type to another, and conversions
> from one pointer-to-function type to another. Conversions from void* to
> a pointer-to-function type simply aren't mentioned.
>
> My conclusion is that the conversion is permitted (it doesn't violate
> any constraint or syntax rule), but its behavior is undefined by
> omission.

I agree with this conclusion.

> Note that the authors of gcc seem to have a different interpretation;
> on the above code, gcc-4.7 with "-std=c99 -pedantic" produces the
> following warning:
>
> warning: ISO C forbids conversion of object pointer to function pointer type [-pedantic]

I interpret gcc's message differently, especially since it is a
warning. Since there is at least potential confusion about
whether the ISO standard allows such conversions, they feel
obliged to give a diagnostic (but without preventing compilation)
to be on the safe side for being a conforming implementation.
When in doubt, issue a diagnostic - no harm for being wrong
on that side of the fence.

> Frankly I think it would make more sense if the conversion *were*
> a constraint violation, or else if its behavior were explicitly
> stated to be undefined (or the result to be implementation-defined
> or unspecified).

The problem is, for this particular case none of those categories
are really quite right. One implementation might think it's okay
and sensible and perfectly acceptable, whereas another might
choose to treat it as an unrecoverable translation failure. It
isn't just executing such a conversion, but even writing an
expression (ie, a cast) that specifies such a conversion, crosses
over into undefined behavior. And, apparently the Standard wants
to give all of that latitude, in both directions (including not
having to issue any diagnostics), for implementations to use as
they see fit. I don't know offhand of any other construct that
falls into exactly this category.

Tijl Coosemans

unread,
Dec 21, 2012, 4:00:36 AM12/21/12
to
As an example POSIX requires that function pointers can be cast to
void* and back. It's marked as an extension of the C standard.

Keith Thompson

unread,
Dec 21, 2012, 5:01:08 AM12/21/12
to
Tim Rentsch <t...@alumni.caltech.edu> writes:

> Keith Thompson <ks...@mib.org> writes:
>
>> Consider the following toy program:
>>
>> int main(void) {
>> void *void_ptr = 0;
>> void (*func_ptr)(void) = (void(*)(void))void_ptr;
>> return 0;
>> }
[...]
>> My conclusion is that the conversion is permitted (it doesn't violate
>> any constraint or syntax rule), but its behavior is undefined by
>> omission.
>
> I agree with this conclusion.
>
>> Note that the authors of gcc seem to have a different interpretation;
>> on the above code, gcc-4.7 with "-std=c99 -pedantic" produces the
>> following warning:
>>
>> warning: ISO C forbids conversion of object pointer to function pointer type [-pedantic]
>
> I interpret gcc's message differently, especially since it is a
> warning. Since there is at least potential confusion about
> whether the ISO standard allows such conversions, they feel
> obliged to give a diagnostic (but without preventing compilation)
> to be on the safe side for being a conforming implementation.
> When in doubt, issue a diagnostic - no harm for being wrong
> on that side of the fence.

gcc issues mere warnings for a lot of things that are constraint
violations. The -pedantic option is intended to enable all the
diagnostics that the standard requires. The -pedantic-errors
option makes those diagnostics fatal. With -pedantic-errors,
the above warning becomes a fatal error.

Tim Rentsch

unread,
Dec 21, 2012, 11:12:30 AM12/21/12
to
> violations. [snip elaboration]

That's good to know, but I'm not sure it changes my reaction.
Do you have some other examples readily at hand?

Keith Thompson

unread,
Dec 21, 2012, 8:58:19 PM12/21/12
to
Tim Rentsch <t...@alumni.caltech.edu> writes:
> Keith Thompson <ks...@mib.org> writes:
[...]
>> gcc issues mere warnings for a lot of things that are constraint
>> violations. [snip elaboration]
>
> That's good to know, but I'm not sure it changes my reaction.
> Do you have some other examples readily at hand?

$ cat c.c
int main(void) {
int x;
int y = &x;
main + 1;
sizeof (void);
}
$ gcc -c -std=c11 -pedantic -c c.c
c.c: In function ‘main’:
c.c:3:13: warning: initialization makes integer from pointer without a cast [enabled by default]
c.c:4:10: warning: pointer to a function used in arithmetic [-pedantic]
c.c:5:13: warning: invalid application of ‘sizeof’ to a void type [-pedantic]
$ gcc -c -std=c11 -pedantic-errors -c c.c
c.c: In function ‘main’:
c.c:3:13: error: initialization makes integer from pointer without a cast
c.c:4:10: error: pointer to a function used in arithmetic [-pedantic]
c.c:5:13: error: invalid application of ‘sizeof’ to a void type
[-pedantic]
$

Tim Rentsch

unread,
Dec 22, 2012, 11:22:38 AM12/22/12
to
Great example! Thank you.
0 new messages