Today I have learned that function pointers and data pointers are not the same and are therefore not compatible with each other (Why are function pointers and data pointers incompatible in C/C++?). My question however is, are different function (non member) pointers compatible with each other (are implemented the same way).
In code:
typedef void(*FuncPtr0)();
typedef void(*FuncPtr1)(int);
FuncPtr0 p0;
FuncPtr1 p1;
p0 = reinterpret_cast<FuncPtr0>(p1); // will this always work, if p1 really
p0(); // points to a function of type FuncPtr0
Thanks for your help!
n3376 5.2.10/6
A function pointer can be explicitly converted to a function pointer of a different type. The effect of calling
a function through a pointer to a function type (8.3.5) that is not the same as the type used in the definition
of the function is undefined. Except that converting a prvalue of type “pointer to T1” to the type “pointer to
T2” (where T1 and T2 are function types) and back to its original type yields the original pointer value, the result of such a pointer conversion is unspecified.
No they are not compatible and will invoke undefined behavior. You will have unspecified results.
In fact you can cast them to each other, but you shouldn't call a function pointer which points to a non-compatible function signature.
For example, see this code :
typedef void(*FuncPtr0)();
void p1f() { std::cout << "ONE"; }
void p2f(int x) { std::cout << "TWO " << x ; }
int main()
{
FuncPtr0 p0 = reinterpret_cast<FuncPtr0>(p2f);
p0();
}
Output
TWO 1
The question is who set argument x to 1 ? It may run but the result is unspecified. In my system the result is something else (garbage) TWO 39.
Related
From what I understand, casting function pointers to different types is allowed by the C++ standard (as long as one never invokes them):
int my_func(int v) { return v; }
int main() {
using from_type = int(int);
using to_type = void(void);
from_type *from = &my_func;
to_type *to = reinterpret_cast<to_type *>(from);
// ...
}
Moreover, there is no undefined behavior if I cast the pointer back to its original type and invoke it.
So far, so good. What about the following, then?
const bool eq = (to == reinterpret_cast<to_type *>(my_func));
Does the address hold, too, after the conversion, or is this not guaranteed by the standard?
While this is irrelevant to the question, a possible scenario is when one goes hard on type erasure. If the address holds, something can be done without having to know the original function type.
From [expr.reinterpret.cast].6 (emphasis mine):
A function pointer can be explicitly converted to a function pointer of a different type.
[...]
Except that converting a prvalue of type “pointer to T1” to the type
“pointer to T2” (where T1 and T2 are function types) and back to its
original type yields the original pointer value, the result of such a
pointer conversion is unspecified.
So, the standard explicitly allows casting function pointers to different FP types and then back. This is an exception to the general rule that reinterpret_casting function pointers is unspecified.
In my understanding, that means to == reinterpret_cast<to_type *>(my_func) need not necessarily be true.
Does C++ guarantee consistent pointer representations when a pointer is casted to other pointer types?
For example, does C++ guarantee anything about the following program?
<stdint.h>
struct Foo {};
struct Bar : Foo {};
int main() {
Bar obj;
Foo * a = &obj;
Bar * b = &obj;
void *c = &obj;
#if MAYBE_UB
int * d = reinterpret_cast<int *>(&obj);
#endif
auto aa = reinterpret_cast<uintptr_t>(a);
auto bb = reinterpret_cast<uintptr_t>(b);
auto cc = reinterpret_cast<uintptr_t>(c);
#if MAYBE_UB
auto dd = reinterpret_cast<uintptr_t>(d); // UB? Not reading the pointee...
#endif
if (aa != bb) printf("bb differs\n");
if (aa != cc) printf("cc differs\n");
#if MAYBE_UB
if (aa != dd) printf("dd differs\n");
#endif
return 0;
}
Does C++ guarantee consistent pointer representations?
No, pointers can be represented in any inconsistent way compiler wants them to. Generally, the language tries to be abstract and talk as little as possible about representation of stuff. It's left for the implementation to figure that out.
Does C++ guarantee consistent pointer representations when a pointer is casted to other pointer types?
No, pointers can be represented in any way they want. Using rocks and sticks, for example.
does C++ guarantee anything about the following program?
TL;DR Well, yes, that the code should compile (ignoring the missing #includfe on top). But there is no guarantee about the output.
Aaanyway, I do not think you are interested in the representation of pointers, but if the value of pointer changes when the pointer value is converted to a different type. This has nothing to do with how the value is "represented", the representation of the pointer can change anyhow the compiler wants to. Well, ok, we know char * and void * have the same representation, from https://eel.is/c++draft/basic.compound#5 :
A pointer to cv void can be used to point to objects of unknown type.
Such a pointer shall be able to hold any object pointer.
An object of type “pointer to cv void” shall have the same representation and alignment requirements as an object of type “pointer to cv char”.
Anyway, we know about void * pointer https://eel.is/c++draft/expr#conv.ptr :
A prvalue of type “pointer to cv T”, where T is an object type, can be converted to a prvalue of type “pointer to cv void”.
The pointer value is unchanged by this conversion.
And this also is relevant:
A prvalue of type “pointer to cv D”, where D is a complete class type, can be converted to a prvalue of type “pointer to cv B”, where B is a base class ([class.derived]) of D.
If B is an inaccessible ([class.access]) or ambiguous ([class.member.lookup]) base class of D, a program that necessitates this conversion is ill-formed.
The result of the conversion is a pointer to the base class subobject of the derived class object.
But there is nothing about the value of the result. It can be the same, it can be different, it's not specified.
The problem arises that your code uses uintptr_t. We know that cstdint has to be the same as in C stdint.h, and from C we know only that C99 7.18.1.4:
The following type designates an unsigned integer type with the property that any valid pointer to void can be converted to this type, then converted back to pointer to void, and the result will compare equal to the original pointer:
uintptr_t
We know nothing about the value of (uintptr_t)(Bar*)&obj. We also do not know if two uintptr_t variables compare equal, even when they were created from two equal void pointers. Only that they compare equal when you convert them back. We fall back to general rule at reinterpret_cast https://eel.is/c++draft/expr#reinterpret.cast-4 :
A pointer can be explicitly converted to any integral type large enough to hold all values of its type. The mapping function is implementation-defined.
[Note 2: It is intended to be unsurprising to those who know the addressing structure of the underlying machine.
— end note]
The "mapping function" can be anything. It can produce inconsistent values that depend on anything - it's implementation defined. So, basically, we know nothing about the resulting uintptr_t values, except there should be some values there.
Anyway, from the above, I conclude that all uintptr_t conversions in your code result in an implementation defined value and can have any value the implementation wants them to have. The program has defined, but implementation-defined behavior - any of those ifs can be true or false, depending on the "pointers to integers mapping function" the compiler uses.
However, we know, that any sane compiler will have a sane "mapping function" that maps pointers to integral types, and this mapping functions will be "unsurprising to those who know the addressing structure of the underlying machine" and will produce consistent values. You might be also interested in this pointer provenance.
As for MAYBE_UB, there is nothing undefined in it, we know that https://eel.is/c++draft/expr#reinterpret.cast-7 :
An object pointer can be explicitly converted to an object pointer of a different type. When a prvalue v of object pointer type is converted to the object pointer type “pointer to cv T”, the result is static_cast<cv T*>(static_cast<cv void*>(v)).
But we know from https://eel.is/c++draft/expr#static.cast-13 :
A prvalue of type “pointer to cv1 void” can be converted to a prvalue of type “pointer to cv2 T”, where T is an object type and cv2 is the same cv-qualification as, or greater cv-qualification than, cv1.
If the original pointer value represents the address A of a byte in memory and A does not satisfy the alignment requirement of T, then the resulting pointer value is unspecified.
Otherwise, if the original pointer value points to an object a, and there is an object b of type T (ignoring cv-qualification) that is pointer-interconvertible with a, the result is a pointer to b.
Otherwise, the pointer value is unchanged by the conversion.
The resulting pointer can be unspecified or unchanged, depending on if int satisfies alignment requirements of &obj, but the code is still defined.
In order to serialize components in my game, I need to be able to access the data in various vectors only given a pointer and a size for the vector.
I want to get the data() pointer from a vector if I have only a void * pointing to the vector. I am attempting to convert from std::vector<T> to std::vector<char> to get the data() pointer. I want to know if the following code is defined behavior and not going to act any different in different situations.
#include <iostream>
#include <vector>
int main()
{
std::vector<int> ints = { 0, 1, 2, 3, 4 };
std::vector<char>* memory = reinterpret_cast<std::vector<char>*>(&ints);
int *intArray = reinterpret_cast<int *>(memory->data());
std::cout << intArray[0] << intArray[1] << intArray[2] << intArray[3] << intArray[4] << std::endl; //01234 Works on gcc and vc++
std::getchar();
}
This seems to work in this isolated case, but I don't know if it will give errors or undefined behavior inside the serialization code.
This is an aliasing violation:
std::vector<char>* memory = reinterpret_cast<std::vector<char>*>(&ints);
int *intArray = reinterpret_cast<int *>(memory->data());
Per [basic.life], accessing memory->data() here has undefined behavior.
The way to get around this is to call ints.data() to obtain a int* pointer to the underlying contiguous array. Afterwards, you are allowed to cast it to void*, char*, or unsigned char* (or std::byte* in C++17).
From there you can cast back to int* to access the elements again.
I don't think that it is UB.
With reinterpret_cast<std::vector<char>*>(&ints), you are casting a vector-object to another vector object of different (and actually incompatible) type. Yet you do not dereference the resulting pointer, and - as both vector objects will very likely have the same aliasing restrictions - the cast will be OK. Cf, for example, this online C++ draft). Note that a vector does not store the data types "in place" but will hold a pointer to the values.
5.2.10 Reinterpret cast
(7) An object pointer can be explicitly converted to an object pointer of
a different type.70 When a prvalue v of type “pointer to T1” is
converted to the type “pointer to cv T2”, the result is static_cast(static_cast(v)) if both T1 and T2 are standard-layout
types ([basic.types]) and the alignment requirements of T2 are no
stricter than those of T1, or if either type is void. Converting a
prvalue of type “pointer to T1” to the type “pointer to T2” (where T1
and T2 are object types and where the alignment requirements of T2 are
no stricter than those of T1) and back to its original type yields the
original pointer value. The result of any other such pointer
conversion is unspecified.
So casting a vector object forth and back should work in a defined manner here.
Second, you cast a pointer that originally points (and is aliased to) int "back" to its original type int. So aliasing is obviously not violated.
I don't see any UB here (unless a vector-object had stricter aliasing rules than a vector-object, which is very likely not the case).
Suppose I have a function void myFun(int*) In C++ what exactly does the following mean
( void(*)(void*) )&myFun
Is it a pointer to a function that takes a (void*) as an argument and returns a void? Is this type of cast permitted?
As it stands, I'm pretty sure it's just not allowed.
If you remove the parens around the initial void to get:
void (*)(void *)
...then yes, it's a pointer to a function returning void and taking a pointer to void as its only argument. To cast to that type, you need to enclose the entire name of the type in parentheses, so you'd get: (void (*)(void *)), which you'd follow by the value being cast, to get:
(void (*)(void *))&myFun;
At least if memory serves, yes, this is allowed, though dereferencing the pointer (i.e., attempting to call the function it points at) via the result may give undefined behavior. In particular, when/if you call the function, it's expecting a pointer to int, and will (presumably) use whatever it points at as an int. If, however, what it points at isn't properly aligned to be used as an int, it's not likely to work as expected.
The cast is permitted, by 5.2.10 (6):
A function pointer can be explicitly converted to a function pointer of a different type. The effect of calling
a function through a pointer to a function type (8.3.5) that is not the same as the type used in the definition
of the function is undefined. Except that converting a prvalue of type “pointer to T1” to the type “pointer to
T2” (where T1 and T2 are function types) and back to its original type yields the original pointer value, the
result of such a pointer conversion is unspecified.
This is equivalent to C 6.3.2.3 (8):
A pointer to a function of one type may be converted to a pointer to a function of another
type and back again; the result shall compare equal to the original pointer. If a converted
pointer is used to call a function whose type is not compatible with the pointed-to type,
the behavior is undefined.
In practice, calling through a function pointer with different but compatible argument types (especially pointer types) usually succeeds, and has the effect of a C-style cast on the arguments. This is by no means guaranteed, however; https://stackoverflow.com/a/189126/567292 discusses a case where gcc decided to make such undefined function pointer cast calls abort.
I know this is a bizarre thing to do, and it's not portable. But I have an allocated array of unsigned ints, and I occasionaly want to "store" a float in it. I don't want to cast the float or convert it to the closest equivalent int; I want to store the exact bit image of the float in the allocated space of the unsigned int, such that I could later retrieve it as a float and it would retain its original float value.
This can be achieved through a simple copy:
uint32_t dst;
float src = get_float();
char * const p = reinterpret_cast<char*>(&dst);
std::copy(p, p + sizeof(float), reinterpret_cast<char *>(&src));
// now read dst
Copying backwards works similarly.
Just do a reinterpret cast of the respective memory location:
float f = 0.5f;
unsigned int i = *reinterpret_cast<unsigned int*>(&f);
or the more C-like version:
unsigned int i = *(unsigned int*)&f;
From your question text I assume you are aware that this breaks if float and unsigned int don't have the same size, but on most usual platforms both should be 32-bit.
EDIT: As Kerrek pointed out, this seems to be undefined behaviour. But I still stand to my answer, as it is short and precise and should indeed work on any practical compiler (convince me of the opposite). But look at Kerrek's answer if you want a UB-free answer.
You can use reinterpret_cast if you really have to. You don't even need to play with pointers/addresses as other answers mention. For example
int i;
reinterpret_cast<float&>(i) = 10;
std::cout << std::endl << i << " " << reinterpret_cast<float&>(i) << std::endl;
also works (and prints 1092616192 10 if you are qurious ;).
EDIT:
From C++ standard (about reinterpret_cast):
5.2.10.7 A pointer to an object can be explicitly converted to a pointer to an object of different type.Except that converting an
rvalue of type “pointer to T1” to the type “pointer to T2” (where T1
and T2 are object types and where the alignment requirements of T2 are
no stricter than those of T1) and back to its original type yields the
original pointer value, the result of such a pointer conversion is
unspecified.
5.2.10.10 10 An lvalue expression of type T1 can be cast to the type “reference to T2” if an expression of type “pointer to T1” can be
explicitly converted to the type “pointer to T2” using a
reinterpret_cast. That is, a reference cast reinterpret_cast<T&>(x)
has the same effect as the conversion
*reinterpret_cast<T*>(&x) with the built-in & and * operators. The result is an lvalue that refers to the same object as the source
lvalue, but with a different type. No temporary is created, no copy is
made, and constructors (12.1) or conversion functions (12.3) are not
called.67)
So it seems that consistently reinterpreting pointers is not undefined behavior, and using references has the same result as taking address, reintepreting and deferencing obtained pointer. I still claim that this is not undefined behavior.