Meet a very weird problem, anyone know what is the reason of this? the code is tested under Visual Studio 2012.
#include <iostream>
struct A {
int a;
};
struct B {
int b;
};
struct C : public A, public B {
int c;
};
int main() {
int C::*p = &C::b;
std::printf("%p\n", &C::b); //00000000
std::printf("%p\n", p); //00000004
return 0;
}
Pointers to members are not just plain C-pointers, so passing them to printf is actually undefined behavior, because printf will believe in the "%p" and C-cast the pointer-to-member to void*.
The only conversions of pointers to members allowed by the standard are listed in paragraph 4.11:
Null-pointer constants can be converted to null member pointer values
B::*T can be converted to D::*T if B is an accessible, nonambiguos and nonvirtual baseclass of D.
Pointers to members can actually have different sizes depending on the classes they point into.
See here for more info on pointer-to-member implementations in MSVC: http://blogs.msdn.com/b/oldnewthing/archive/2004/02/09/70002.aspx
Note the possibly unexpected result of:
printf("typeid(&C::b) = %s\n",typeid(&C::b).name());
printf("typeid(&B::b) = %s\n",typeid(&B::b).name());
Which on VS2010 yields:
typeid(&C::b) = int B::*
typeid(&B::b) = int B::*
This indicates that the resulting type for &C::b is a member pointer on type B. Since C is derived from B the pointer is freely convertible to a member pointer on type C. This explains the difference between the two values you see. &C::b is a member function pointer on type B, int C::*p is a member function pointer on type C.
int B::*p_b = &B::b;
int C::*p_c = p_b;
printf("p_b = %p\n", p_b);
printf("p_c = %p\n", p_c);
Related
I am trying to understand some details of static_cast.
Please have a look at the following code,
struct A
{
int data = 0;
};
void foo(const A* a)
{
(*static_cast<A**>(static_cast<void*>(&a)))->data = 1;
}
void bar(const A* a)
{
const_cast<A*>(a)->data = 1;
}
int main()
{
A a;
foo(&a);
return a.data;
}
Is the function foo valid C++ code?
Is there any valid usage that gives a different result with foo vs. bar?
Both functions are valid C++ and have well-defined behavior (modifying the data member of the A object created in main) in C++11 and later.
You are allowed to obtain a pointer to non-const from a pointer to const object type either directly with const_cast or indirectly with static_cast going through void* as you are doing in foo. That in itself is not a problem:
The cast to void* is possible because &a is a pointer to const A* which is not (top-level) cv-qualified. The cast from void* to A** is possible because void* can be cast to any object pointer type. Dereferencing the result is accessing the const A* object through a pointer to A*, but that is ok because the types are similar. (The last part seems to have been an aliasing rule violation prior to C++11, making this undefined behavior.)
However, modifying a const qualified object through a pointer to non-const obtained in such a way causes undefined behavior.
Since the object A a; that you are passing to the function is not const qualified, there is no problem.
But I think it is obvious why using such functions is dangerous. Changing the declaration A a; to const A a; will still compile because there is no type mismatch, but will have undefined behavior.
Both functions do exactly the same thing in all situations, as far as I can tell.
Is the following code safe :
#include <iostream>
#include <cstdint>
struct A{
int i = 0;
virtual int foo() {return i;}
};
struct B : A{
int foo() override {return i+2;}
};
using handle_t = std::uintptr_t;
handle_t get(B& a){
return reinterpret_cast<handle_t>(&a);
}
void use(handle_t h){
auto p= reinterpret_cast<A*>(h); //
std::cout << p->foo() << "\n";
}
int main(int argc, char *argv[])
{
B a;
auto h = get(a);
use(h);
return 0;
}
CppReference's page says one can :
reinterpret_cast from B* to std::uintptr_t
reinterpret_cast from std::uintptr_t to B* (because it's the same type back and forth)
reinterpret_cast from B* to A*
So, is it safe to merge the last two ?
I don't know what "safe" means, but the behavior of a program that uses this code is undefined. You can convert a pointer into an integer type that's large enough to hold the value, and you can convert that value back into a pointer with the same type as the original. The code in the question doesn't do that: it converts the value into a pointer with a different type from the original.
In that particular case the code is safe. That's because the safety of upcasting. This is always allowed for public inheritance, without an explicit type cast.
In short you're just force something could be consider as implicit.
The passage of B type's address with a uintptr_t is useless also but allowed because "the same type".
Edit
About uintptr_t.
Integer type capable of holding a value converted from a void pointer and then be converted back to that type with a value that compares equal to the original pointer.
Note "compares equal to the original pointer".
Context: C++
Consider the example below
class TestClass
{
private:
int A[];
int *B;
public:
TestClass();
};
TestClass::TestClass()
{
A = 0; // Fails due to error: incompatible types in assignment of `int' to `int[0u]'
B = 0; // Passes
}
A = 0 fails but B = 0 succeeds. What's the catch? What exactly is A? A constant pointer? How do I initialize it then?
The question "what is the difference between int* and int[]?" is a less trivial question than most people will think of: it depends on where it is used.
In a declaration, like extern int a[]; it means that somewhere there is an array called a, for which the size is unknown here. In a definition with aggregate initialization, like int a[] = { 1, 2, 3 }; it means an array of a size I, as programmer, don't want to calculate and you, compiler, have to interpret from the initialization. In a definition without initialization, it is an error, as you cannot define an array of an unknown size. In a function declaration (and/or) definition, it is exactly equivalent to int*, the language specifies that when processing the types of the arguments for functions, arrays are converted into pointers to the contained type.
In your particular case, as declaration of members of a class, int a[]; is an error, as you are declaring a member of an incomplete type. If you add a size there, as in int a[10] then it becomes the declaration of an array of type int and size 10, and that will reserve space for 10 int inside each object of the class. While on the other hand, int *b will only reserve space for a pointer to integers in the class.
The only difference between them is that int A[] in a class would not compile, and should not compile!
Comeau C++ compiler gives this error:
"ComeauTest.c", line 4: error:
incomplete type is not allowed
int A[];
^
Wikipedia says,
Comeau C/C++ has been regarded as the
most standards-conformant C++
compiler.
I therefore would suggest : Don't write such code even if your compiler compiles it.
A is an array, and in C++ you need to specify the array size when you define the variable itself i.e. you need to do something like int A[10]. Then you can access individual elements using A[0], A[1] etc. B is a pointer, so doing B=0; sets the pointer to NULL. If you don't want to specify the size at compile time, but still want array like syntax you can use std::vector<int>.
int A[] is an memory range inside the class instance, where int *B is a pointer that you may or may not initialize later.
so
A=0 means that you want to change a pointer that cannot be changed,
where
B=0 means that you are changing a pointer to point to 0x00000000
A[] is an array with its size undefined. You need to declare it like this:
int A[SIZE];
then initialize it like this:
A[0] = 0,
A[1] = 5,
etc
long answer short, A is of type array
and B is of type pointer to int.
A[] initializes an array of zero size, which you cannot assign a value to.
Btw, you can ask C++ yourself with:
#include <iostream>
#include <typeinfo>
using namespace std;
class TestClass{
private:
int A[];
int* B;
public:
TestClass();
};
TestClass::TestClass(){
cout<<"Type A: "<<typeid(A).name()<<endl;
cout<<"Type B: "<<typeid(B).name()<<endl;
}
int main(int argc, char** argv){
TestClass A;
return 0;
}
The code snapshot you have there should not compile since A[] is effectively a zero sized array, thus an incomplete type, which is not allowed in class specifications.
For e.g. Visual Studio fails to compile this with these errors:
1>...\test.h(4) : warning C4200:
nonstandard extension used :
zero-sized array in struct/union 1>
Cannot generate copy-ctor or
copy-assignment operator when UDT
contains a zero-sized array
1>...\test.h(5) : error C2229: class
'TestClass' has an illegal zero-sized
array
To initialize and use A[] you must declare it as static and initialize it with file scope like this:
class TestClass
{
private:
static int A[];
int *B;
public:
TestClass();
};
TestClass::TestClass()
{
//A = 0; Yes this is wrong, see below.
B = 0; // Passes
}
int TestClass::A[] = {1,2,3};
As for A = 0 this is wrong too and Visual Studio will complain with this error, which is self explanatory:
1>..\test.h(13) : error C2440: '=' : cannot convert from 'int' to 'int []'
1> There are no conversions to array types, although there are conversions to references or pointers to arrays.
LE: Also see David Rodríguez - dribeas answer for a complete interpretation of zero sized arrays.
I'm having problems getting this to work,
class A {
public:
A(int n) {
a = n;
}
int getA() {
return a;
}
private:
int a;
};
int main(){
A* a[3];
A* b[3];
for (int i = 0; i < 3; ++i) {
a[i] = new A(i + 1);
}
void * pointer = a;
b = (A* [])pointer; // DOESNT WORK Apparently ISO C++ forbids casting to an array type ‘A* []’.
b = static_cast<A*[]>(pointer); // DOESN'T WORK invalid static_cast from type ‘void*’ to type ‘A* []’
return 0;
}
And i can't use generic types for what i need.
Thanks in advance.
Arrays are second-class citizen in C (and thus in C++). For example, you can't assign them. And it's hard to pass them to a function without them degrading to a pointer to their first element.
A pointer to an array's first element can for most purposes be used like the array - except you cannot use it to get the array's size.
When you write
void * pointer = a;
a is implicitly converted to a pointer to its first element, and that is then casted to void*.
From that, you cannot have the array back, but you can get the pointer to the first element:
A* b = static_cast<A*>(pointer);
(Note: casting between pointers to unrelated types requires a reinterpret_cast, except for casts to void* which are implicit and from void* to any other pointer, which can be done using a static_cast.)
Perhaps you mean to do
memcpy(b, (A**)pointer, sizeof b);
?
A static_cast version is also possible.
If you have a void* pointer to Derived class that inherits from both BaseA and BaseB, how does the compiler cast the void* pointer to BaseA* (or BaseB*) without knowing that the void* pointer is of type Derived?
It doesn't. The only guarantee when casting to and from a void* using a static_cast is:
A value of type pointer to object converted to "pointer to cv void" and back to the original pointer type will have its original value (C++03 §5.2.9/10).
For example, the following code is incorrect because the void* is cast to a type other than the original pointer type (the cast sequence is B1* -> void* -> B2*):
struct B1 { int i; };
struct B2 { int j; };
struct D : B1, B2 { };
D x;
B1* b1ptr = &x;
void* voidptr = b1ptr;
B2* b2ptr = static_cast<B2*>(voidptr);
Attempting to use b2ptr here would result in undefined behavior. The only type to which you can safely cast voidptr is B1*, since that is the type from which the void* was obtained (well, or to a char*, since anything can be accessed via a char*).
The compiler doesn't cast the void* pointer to anything -- you, the programmer, do.
In order to do anything useful with a void* pointer, you need to explicitly cast it to a non-void* pointer, and if you're wrong about what type the pointer actually points to, you enter Undefined Behavior City.