Возвращение массивов из функций в переполнении стека

Добрый день 🙂 Я работаю над кодом для получения углов тангажа, рыскания и крена от акселерометра и гироскопа. Чтобы создать более понятный код, за которым легко следовать, я прибег к созданию двух разных функций. Один для гироскопа, который вычисляет Pitch Yaw and Roll, и другой для акселерометра, который также делает то же самое.

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;
}

Как вы можете видеть выше, я сохранил выходные данные функций в массив для передачи в основную функцию. В основном указатель пропущен. Однако при доступе к значениям из переданного указателя вместо него печатались постоянные значения нежелательной почты (не меняющиеся при перемещении IMU) (например, 2.38221e-44 и -3.84146e-06). Я проверил вывод функций для гироскопа и акселерометра, напечатав значения в этих функциях, и они были в порядке.

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;

}
}

Я не мог найти то, что я сделал не так с моим кодом. До этого я обращался ко многим ссылкам. Однако я все еще не мог решить это. Ваша помощь будет очень ценится 🙂

2

Решение

То, что вы делаете, никогда не сработает, так как вы возвращаете указатель на стек readGyro а также readAccel, Когда эти функции завершаются, эта часть стека восстанавливается, и у вас остается неопределенное поведение.

Одним из способов было бы выделить массив и вернуть его, но затем вы обременяете себя тем, что также должны удалить это распределение.

Вы можете создать 2 структуры для требуемых параметров и передать их в свои функции. Таким образом, функции могут писать куда-то, что будет сохраняться после их возврата.

например

#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;
}
}

Обратите внимание, что это также более читабельно, чем ваш пример, поскольку в нем каждая переменная явно указана, а не выражается в виде смещений от возвращаемого значения. Назначение новым переменным, позволяющее вам понять, что означают значения, больше не требуется.

Эти две структуры фактически идентичны и поэтому могут быть объединены в одну общую структуру, если это необходимо.

1

Другие решения

Встроенные в C ++ массивы наследуются от C и не обязательно работают так, как ожидает большинство людей. Допустим, у вас есть такая функция:

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

что это на самом деле делает, это вернуть адрес размещенной в стеке переменной vec; к несчастью vec выйдет из области видимости после завершения функции, и возвращенный адрес будет бессмысленным.

Обходной путь — обернуть массив в struct, который Можно быть возвращены по значению. Вы можете либо определить свой собственный, либо использовать std::array из стандартной библиотеки C ++, вот так:

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

То, что вы делаете, это возвращение адрес массива, который является локальным для функции, которая уничтожается при выходе из функции.

Если вы хотите массив для индексации, я бы порекомендовал использовать std::array,

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

Но лучше было бы использовать struct как это.

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

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

При копировании массива в качестве элемента данных элементы массива будут копироваться по одному вместо копирования затухшего указателя. Это позволяет избежать возврата адреса локальной переменной.

struct float3{
float fs[3];
};

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

Но используя std::array<float, 3> как сказали другие ответы, очень уместно. Вам не нужно изобретать другое колесо.

Если вам нравится получать результат через параметры, используйте ссылку вместо указателя, чтобы избежать потери информации о размере (затухает до указателя). Это также гарантирует только тип float[3] передается.

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