简体   繁体   中英

Casting a Vec* to a double*

I am trying to understand a framework I have been provided with and slowly debugging it. In my case I have an array of Vec, memorized with a pointer:

Vec *cx =....;

My Vec is a struct defined as follows:

struct Vec {
    double x, y, z;                  // position, also color (r,g,b)
    Vec(double x_ = 0, double y_ = 0, double z_ = 0){ x = x_; y = y_; z = z_; }
    Vec operator+(const Vec &b) const { return Vec(x + b.x, y + b.y, z + b.z); }
    Vec operator-(const Vec &b) const { return Vec(x - b.x, y - b.y, z - b.z); }
    Vec operator-() const { return Vec(-x, -y, -z); }
    Vec operator*(double b) const { return Vec(x*b, y*b, z*b); }
    Vec mult(const Vec &b) const { return Vec(x*b.x, y*b.y, z*b.z); }
    Vec& norm(){ return *this = *this * (1 / sqrtf(x*x + y*y + z*z)); }
    double dot(const Vec &b) const { return x*b.x + y*b.y + z*b.z; } // cross:
    Vec operator%(Vec&b){ return Vec(y*b.z - z*b.y, z*b.x - x*b.z, x*b.y - y*b.x); }
    double max() const { return x>y && x>z ? x : y > z ? y : z; }
};

I would like to know what actually happens if I cast an array of Vec to an array of doubles. In practice:

double * cast = (double*)cx;

What does it give me back?

In order to understand, I tried to do this:

Vec boh = Vec(1.0, 1.0, 1.0);
Vec boh2 = Vec(2.0, 2.0, 2.0);
Vec * try = new Vec[2];
try[0] = boh;
try[1] = boh2;
double* try2= (double*)try;

I realize that try points to the first Vec, which is boh1, so if I go debugging, I see the x,y,z values set to 1. Likewise try2 should be pointing to "something" related to boh1, but the debug shows me that try2 has the value 1.000000. So, what is actually happening?

EDIT: "try" can't be used as name of a variable in c++.

It will still give you a pointer to Vec , which is not a double at all. What the casting does is telling the compiler to completely disregard the actual type of try , effectively pretending that try is something it's not.

Using try2 will lead to undefined behavior .

What is "actually" happening is that since the data members of your struct is 3 doubles, your colleague is counting on C behavior where they are looking at the memory as a double instead of a Vec . In C, it would act like a "union", that is the struct lay out in memory is just 3 doubles one after the other. In your case (using C++), it just so happens (see EDIT) to act like a union. So you are accessing the 1.0 from boh (as you call it boh1 ). Others say, the behavior is undefined. I believe "implementation dependant" might be more accurate, but I will defer to them (unless I can find the reference).

Regardless, your example of code is ripe for refactoring if you can. If your colleague is relying upon this behavior, they should clearly identify that so that others don't come after and change the structure.

EDIT : Okay, I found a reference that suggests this is NOT undefined behavior (unless I am misunderstanding the reference). The struct is a POD struct per Section 9 (ss7,10) of the C++ spec. POD (Plain old data) layout is described in 9.2 (ss13).

9 ss10
A POD struct110 is a non-union class that is both a trivial class and a standard-layout class, and has no non-static data members of type non-POD struct, non-POD union (or array of such types). Similarly, a POD union is a union that is both a trivial class and a standard-layout class, and has no non-static data members of type non-POD struct, non-POD union (or array of such types). A POD class is a class that is either a POD struct or a POD union.

9 ss7
A class S is a standard-layout class if it:
—(7.1) has no non-static data members of type non-standard-layout class (or array of such types) or reference,
—(7.2) has no virtual functions (10.3) and no virtual base classes (10.1),
—(7.3) has the same access control (Clause 11) for all non-static data members,
—(7.4) has no non-standard-layout base classes,
—(7.5) has at most one base class subobject of any given type,
—(7.6) has all non-static data members and bit-fields in the class and its base classes first declared in the same class, and
—(7.7) has no element of the set M(S) of types (defined below) as a base class.109

9.2 ss13
Nonstatic data members of a (non-union) class with the same access control (Clause 11) are allocated so that later members have higher addresses within a class object. The order of allocation of non-static data members with different access control is unspecified (Clause 11). Implementation alignment requirements might cause two adjacent members not to be allocated immediately after each other; so might requirements for space for managing virtual functions (10.3) and virtual base classes (10.1).

Therefore, I suspect your colleague is relying upon POD layout in order to get "union" like behavior. I still think this is a very bad practice. If it can't be avoided, a compile time check should be added or the structure should be documented to identify this requirement.

As others pointed out, what you're doing is very wrong - I won't repeat the details again.


Why you're seeing 1.0000 in your debugger? For that, here's why:

try is a pointer, and as such it's value after Vec * try = new Vec[2]; is some memory address where the array was allocated.
When you do double* try2 = (double*)try; you're telling the compiler to take try s value and copy it to try2 . Just that - the value, which is a memory address is being copied.

After that, if you watch what value exist at *try2 , you'll see a textual presentation of a double value at the address pointed to by try2 . Since you copied a Vec struct to that address, the value you'll see is coming from that data, which has a double at relative memory location 0 - that's the x member of Vec , which you assigned 1.0 to.

Again, all of this could and should be considered coincidental and you shouldn't do such casting to begin with.

The behaviour of your program is undefined.

Essentially, sizeof(double) is not the same as sizeof(Vec) so all the pointer arithmetic and array indexing will break down following your C-style cast.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM