8

I'm trying to return float x, y and z angle values for a body object from my ODE (open dynamics engine) simulation.

float* Creature::eulerAngles(const float &q0, const float &q1, const float &q2, const float &q3){

    float angles[3] = {atan2(2 * (q0*q1 + q2*q3), 1 - 2 * (q1*q1 + q2*q2)),
                      asin( 2 * (q0*q2 - q3*q1)),
                      atan2(2 * (q0*q3 + q1*q2), 1 - 2 * (q2*q2 + q3*q3))};
    return angles;
}

Because dBodyGetQuaternion returns 4 const float quaternions I need to then get the rotations and I've had immense difficulty trying to get it to compile. Now it does compile but I'm getting this warning.

Could anyone explain to me why and what it means please?

2
  • 1
    The error message is pretty clear. You're returning the address of angles, but after you return, angles no longer exists. So what's the caller supposed to do with the address of an object that no longer exists? Commented Apr 22, 2016 at 4:49
  • Okay, thanks for clearing things up:) Commented Apr 22, 2016 at 5:06

3 Answers 3

21
float angles[3] = { ... };

defines a local array.

The statement

return angles;

returns a pointer to the first element of the array.

However, the array is destructed as soon as the function returns. Hence, the returned pointer is a dangling pointer.

That's what the compiler is warning you about. If you dereference the returned pointer in the calling function, you invoke undefined behavior.

In order to return a pointer to an array that will remain valid after the function returns, you need to allocate dynamic memory and return the dynamic memory.

float* Creature::eulerAngles(const float &q0, const float &q1,
                             const float &q2, const float &q3)
{
   float* angles = new float[3];
   angles[0] = atan2(2 * (q0*q1 + q2*q3), 1 - 2 * (q1*q1 + q2*q2));
   angles[1] = asin( 2 * (q0*q2 - q3*q1));
   angles[2] = atan2(2 * (q0*q3 + q1*q2), 1 - 2 * (q2*q2 + q3*q3));

   return angles;
}

Keep in mind that if you do the above, you'll have to make sure to call delete [] on the returned pointer in the calling function.

To avoid the hassles of manually allocating and deallocating memory, you can use std::vector<float> as your return type.

std::vector<float> Creature::eulerAngles(const float &q0, const float &q1,
                                         const float &q2, const float &q3)
{
   std::vector<float> angles(3);
   angles[0] = atan2(2 * (q0*q1 + q2*q3), 1 - 2 * (q1*q1 + q2*q2));
   angles[1] = asin( 2 * (q0*q2 - q3*q1));
   angles[2] = atan2(2 * (q0*q3 + q1*q2), 1 - 2 * (q2*q2 + q3*q3));

   return angles;
}

With this, memory management is done automatically for you.

Since the size of the array is fixed at 3, using std::array<float, 3> is better than using std::vector<float>:

std::array<float, 3> Creature::eulerAngles(const float &q0, const float &q1, const float &q2, const float &q3)
{
   std::array<float, 3> angles;
   angles[0] = atan2(2 * (q0*q1 + q2*q3), 1 - 2 * (q1*q1 + q2*q2));
   angles[1] = asin( 2 * (q0*q2 - q3*q1));
   angles[2] = atan2(2 * (q0*q3 + q1*q2), 1 - 2 * (q2*q2 + q3*q3));

   return angles;
}
Sign up to request clarification or add additional context in comments.

3 Comments

array<float, 3> might be appropriate since it seems the same size is returned every time
This works great thank you for an in depth explanation. I've only been using C++ for the past few months and still get confused with how pointers work so appreciate the helpful advice.
@Jade, I am happy to help. Happy coding.
2

The warning says exactly what's wrong: You are returning a pointer to the local array angles.

Local variables, doesn't matter it they are simple int variables, or arrays like yours, go out of scope when their function returns. That means they kind of disappear. Having a pointer to such a variable means you can't use that pointer anymore, as it no longer points to memory occupied by the variable. Using it will lead to undefined behavior.

There are two solutions to your problem: The first is to allocate the array dynamically using new[], and return that pointer. Memory allocated with new[] never goes out of scope until you delete[] it.

The second solution is to define the array in the calling function, and pass a pointer to it as an argument and have your function fill it in.

Since I missed that this was a C++ question, there is a third solution that I rather recommend: Using std::array. Then you can declare the array locally inside the function, and return the object and the object and compiler will make sure the data is copied as needed.

1 Comment

This is a C++ question, so malloc should not really be recommended
1

You need to stick the result on the heap so that it survives the return of the local function:

float* Creature::eulerAngles(const float &q0, const float &q1, const float &q2, const float &q3){

float * angles = new float[3]{atan2(2 * (q0*q1 + q2*q3), 1 - 2 * (q1*q1 + q2*q2)),
        asin( 2 * (q0*q2 - q3*q1)),
        atan2(2 * (q0*q3 + q1*q2), 1 - 2 * (q2*q2 + q3*q3))};
return angles;
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.