Returning Arrays from functions in C++

Good day :) I am working on a code for obtaining pitch, yaw and roll angles from an Accelerometer and a Gyroscope. To create a cleaner looking code that is easy to follow, I resorted to creating two different functions. One for the gyroscope that calculates the Pitch Yaw and Roll, and another for the accelerometer which also does the same thing.

float *readGyro(){
    /*get data from sensor here*/
    float gyroAngles[4]={gyroPitch,gyroRoll,gyroYaw};
    float* gyroPRY=gyroAngles;
    return gyroPRY;
}

float *readAccel(){
    /*get data from sensor here*/
    float accelAngles[4]={accelPitch,accelRoll,accelYaw};
    float* accelPRY=accelAngles;
    return accelPRY;
}

As you can see above, I stored the outputs of the functions into an array to be passed onto the main function. Basically the pointer is passed. However upon accessing the values from the pointer passed, constant junk values (not changing as I move the IMU) were printed instead (eg. 2.38221e-44 and -3.84146e-06). I checked the output of the functions for the gyro and the accelerometer by printing the values within those functions and they were fine.

int main(void){
    float *accelData;
    float *gyroData;

    while(1){
    accelData=readGyro();
    gyroData=readAccel();

    float accelPitch=*(accelData);
    float accelRoll=*(accelData+1);
    float accelYaw=*(accelData+2);

    float gyroPitch=*(gyroData);
    float gyroRoll=*(gyroData+1);
    float gyroYaw=*(gyroData+2);

    cout << "AccelPitch=" << accelPitch <<endl;
    cout << "AccelRoll=" << accelRoll <<endl;
    cout << "AccelYaw=" << accelYaw <<endl;
    cout << "GyroPitch=" << gyroPitch <<endl;
    cout << "GyroRoll=" << gyroRoll <<endl;
    cout << "GyroYaw=" << gyroYaw <<endl;

    }
}

I could not find what I did wrong with my code. Prior to this I consulted many references. However I still couldn't solve it. Your help would be very appreciated :)

Answers


What you are doing will never work, as you are returning a pointer onto the stack of readGyro and readAccel. When those functions exit, that part of the stack is reclaimed and you are left with undefined behaviour.

One way would be to allocate the array and return it, but then you burden yourself with having to delete that allocation as well.

You could create 2 structs for the required parameters and pass them into your functions. That way the functions can write somewhere that will persist after they return.

e.g.

#include <iostream>

using std::cout;
using std::endl;

struct gyro_data_t
{
    float pitch;
    float roll;
    float yaw;
};

struct accel_data_t
{
    float pitch;
    float roll;
    float yaw;
};

void readGyro(gyro_data_t* gd)
{
    /*get data from sensor here*/
    gd->pitch = 1.0f;
    gd->roll = 1.1f;
    gd->yaw = 1.2f;
}

void readAccel(accel_data_t* ad)
{
    /*get data from sensor here*/
    ad->pitch = 1.0f;
    ad->roll = 1.1f;
    ad->yaw = 1.2f;
}

int main(void)
{
    accel_data_t accelData;
    gyro_data_t gyroData;

    while(1)
    {
        readGyro(&gyroData);
        readAccel(&accelData);

        cout << "AccelPitch=" << accelData.pitch << endl;
        cout << "AccelRoll=" << accelData.roll << endl;
        cout << "AccelYaw=" << accelData.yaw << endl;
        cout << "GyroPitch=" << gyroData.pitch << endl;
        cout << "GyroRoll=" << gyroData.roll << endl;
        cout << "GyroYaw=" << gyroData.yaw << endl;
    }
}

Note how this is also more readable than your example as it names each variable explicitly, rather than expressing them as offsets from the returned value. The assignment to new variables to allow you to comprehend what the values mean is no longer required.

The two structs are actually identical and so could be condensed into one general struct if required.


C++'s built-in arrays are inherited from C, and don't necessarily work the way most people expect. Let's say you have a function like this:

float* get_vec() {
    float vec[3] = { 1.0f, 2.0f, 3.0f };
    return vec;
}

what this actually does is return the address of the stack-allocated variable vec; unfortunately vec will go out of scope when the function ends, and the returned address will be meaningless.

The way round this is to wrap the array up in a struct, which can be returned by value. You can either define your own, or use std::array from the C++ standard library, like so:

std::array<float, 3> get_vec() {
    std::array<float, 3> vec = { 1.0f, 2.0f, 3.0f };
    return vec;
}

What you are doing is returning the address of the array that is local to the function which gets destroyed when the function exits.

If you want an array for indexing reasons then I would recommend using std::array.

std::array<float, 3> readGyro() {
    /*get data from sensor here*/
    return {gyroPitch, gyroRoll, gyroYaw};
}

But better would be to use a struct like this.

struct angles
{
    float pitch;
    float roll;
    float yaw;
};

angles readGyro() {
    /*get data from sensor here*/
    return {gyroPitch, gyroRoll, gyroYaw};
}

Wrap array as a struct to return by value

While copying the array as data-member, the array elements will be copied one by one instead of copying the decayed pointer. This avoids returning the address of local variable.

struct float3{
    float fs[3];
};

float3 readGyro(){
    float3 f3 = {};
    f3.fs[0] = gyroPitch;
    f3.fs[1] = gyroRoll;
    f3.fs[2] = gyroYaw;
    return f3;
}

But using the std::array<float, 3> as other answers said is much appropriate. You don't need to invent another wheel.

Get the result via pass by reference

If you like get result through parameters, use reference instead of pointer to avoid lost the size information (decayed to pointer). This also ensures only type float[3] is passed.

void readGyro(float (&fs)[3])
{        
    fs[0] = gyroPitch;
    fs[1] = gyroRoll;
    fs[2] = gyroYaw;
}

Need Your Help

InstallUtil Publishing WMI Schema to 64 Bit Directory Instead of 32 Bit Directory

.net windows-server-2008 wmi 64-bit

This is similar to this question, but it doesn't look like a good solution was ever determined, so I'm opening a new one with clarified details.