3

Is casting a function pointer to int then casting it back and making an indirect call an undefined behavior in C++? Like this:

void f()
{
    puts("Hello\n");
}


int pf = (int)f;
//...some time later
typedef void (*PVoidFunc)();
(*(PVoidFunc)pf)();

Assume that int and pointer are the same size.

EDIT: The architecture is 32-bit - it's vanilla Android.

It's important, though, that we're talking function pointers, not data pointers. The underlying issue has to do with the way Thumb functions have two addresses - their true starting address (2-aligned) and the value you need to BLX to (address OR 1). I think GCC is trying to be smart about that, but it comes out rather bogus.

Naturally, the issue is that this doesn't work for me in one particular case, but works in a very similar one :) But I'm wondering if this is a compiler bug, compiler's attempt to work around the thorny underlying problem, or undefined behavior being undefined.

Seva Alekseyev
  • 59,826
  • 25
  • 160
  • 281
  • Given this uses C style casts it becomes [quite complicated](http://en.cppreference.com/w/cpp/language/explicit_cast) – Mgetz Mar 01 '14 at 00:32
  • 1
    @Keats: No, I don't think it's a duplicate. The rules for pointer-to-integer and integer-to-pointer conversions are different for object pointers vs. function pointers. – Keith Thompson Mar 01 '14 at 01:01
  • @Keats: Correction: The rules are different in C, but C++ uses the same wording for both. Still the [other question](http://stackoverflow.com/q/3567905/827263) doesn't talk about function pointers. – Keith Thompson Mar 01 '14 at 01:07
  • 1
    @Keith Thompson but it talks about pointer in general, and both answers (here and there) quote the same standard section. I understand your position but am still voting for a duplicate. – KeatsPeeks Mar 01 '14 at 01:10
  • I vote to reopen the question because it is about function pointers on ARM processors, which is tricky because ARM processors have Thumb mode and branch instruction arguments differ depending on whether the callee is ARM or Thumb. See https://developer.arm.com/docs/dui0471/k/interworking-arm-and-thumb/pointers-to-functions-in-thumb-state – Serge Rogatch Feb 10 '17 at 14:33

2 Answers2

3

The answer depends on the language (C vs C++), on the version of the language (C++98/03 vs C++11), on other standards (e.g., POSIX), and on the compiler.

In C99 and earlier, converting a function pointer to/from a void* pointer is undefined behavior. UB can be a huge negative, but in this case it means that an implementation is free to provide the ability to convert between function pointers and pointers with no diagnostic whatsoever.

In POSIX, converting a function pointer to/from a void* pointer is required behavior. This works nicely with C because no diagnostic is required for undefined behavior.

In C++98/03, converting a function pointer to/from a void* pointer is illegal behavior. A compliant implementation could generate the code that does what the author wants, but it also has to issue a diagnostic of some sort.

In C++11, converting a function pointer to/from a void* pointer is conditionally-supported behavior. An implementation that supports these conversions can do so without any diagnostic. An implementation that doesn't (for example, a compiler for a Harvard architecture machine) can reject it out of hand.


Converting to/from an int? That's dubious. You should use a type that can handle a void* pointer rather than int.

David Hammen
  • 32,454
  • 9
  • 60
  • 108
  • I can retry with a void *, but I think the results would be similar. It all boils down to an ARM-specific question to which no good answer exists: what's the value of a pointer to a Thumb function? – Seva Alekseyev Mar 01 '14 at 02:59
0

Short answer: It's well-defined if int is large enough to hold the value. If int isn't large enough to hold the value, then the compiler has to at least issue a warning.

Details:

Section 5.4 discusses C-style cast notation.

§5.4/4

The conversions performed by

— a const_cast (5.2.11),

— a static_cast (5.2.9),

— a static_cast followed by a const_cast,

— a reinterpret_cast (5.2.10), or

— a reinterpret_cast followed by a const_cast`,

can be performed using the cast notation of explicit type conversion... If a conversion can be interpreted in more than one of the ways listed above, the interpretation that appears first in the list is used

The conversion of a function pointer to an integral type requires reinterpret_cast because it cannot be performed by any other cast.

Section 5.2.10 of the standard discusses reinterpret_cast.

§5.2.10/4

A pointer can be explicitly converted to any integral type large enough to hold it. The mapping function is implementation-defined.

§5.2.10/5

A value of integral type or enumeration type can be explicitly converted to a pointer. A pointer converted to an integer of sufficient size (if any such exists on the implementation) and back to the same pointer type will have its original value; mappings between pointers and integers are otherwise implementation-defined. [ Note: Except as described in 3.7.4.3, the result of such a conversion will not be a safely-derived pointer value. — end note ]

You can go read §3.7.4.3 if you want, but it doesn't apply to function pointers. AFAIK, its main purpose is to allow implementation of garbage collection, which applies only to objects and not functions.

If int is too small to hold the value, then there is nothing in the standard that allows the conversion. The compiler may still allow the conversion, but it must issue a diagnostic.

Brian Bi
  • 111,498
  • 10
  • 176
  • 312