En primer lugar, la palabra clave volatile
se usa en el contexto de hilos, por lo que si no los usas, no va a ser necesaria.
Más adelante, asignas una dirección de memoria de una variable todavía no declarada a otra variable, por lo que solo hay que cambiar el orden de las instrucciones.
Finalmente, devuelves un puntero, lo que es totalmente legal, pero ese puntero apunta a una variable que es local y que al finalizar la llamada a la función dicha variable va a ser destruida.
Lo habitual sería crear el vector en memoria dinámica, pero como no es lo que buscas, la otra solución es no devolver un puntero al vector, sino el vector en sí mismo.
Pero va a ocurrir otro problema: cuando devuelves un dato de un tipo primitivo (o estructuras), lo que devuelves es una copia del dato que se va a destruir al finalizar la función, mientras que si devuelves un vector, que en esencia es lo mismo que devolver una dirección de memoria (o puntero), no se realiza dicha copia.
La solución a esto es envolver el vector en una estructura, que al devolverla sí será copiada antes de ser destruida en la pila.
Con todo, el código quedaría:
struct velocidades
{
uint8_t array_datos[3];
};
struct velocidades velocidad(uint16_t velocidad_0_to_1023, uint8_t direccion)
{
struct velocidades envoltura_velocidad;
if (velocidad_0_to_1023 > 1023) velocidad_0_to_1023 = 1023;
uint8_t velocidad_L = (uint8_t) 0x00FF & velocidad_0_to_1023;
uint8_t velocidad_H = (uint8_t) ((0x00FF & (velocidad_0_to_1023 >> 8)) | direccion);
envoltura_velocidad.array_datos[0] = 0x20;
envoltura_velocidad.array_datos[1] = velocidad_L;
envoltura_velocidad.array_datos[2] = velocidad_H;
return envoltura_velocidad;
}
Fuentes:
https://stackoverflow.com/questions/19042552/returning-a-pointer-of-a-local-variable-c
https://stackoverflow.com/questions/4570366/how-to-access-a-local-variable-from-a-different-function-using-pointers/4581061#4581061
Edición
Como preguntas sobre el uso de memoria dinámica, te pongo el código, que hace uso de la función malloc
para reservar memoria montón (o heap), aunque no era lo que buscabas en un principio.
uint8_t * velocidad(uint16_t velocidad_0_to_1023, uint8_t direccion)
{
uint8_t * velocidades = (uint8_t *) malloc(3);
if (velocidad_0_to_1023 > 1023) velocidad_0_to_1023 = 1023;
uint8_t velocidad_L = (uint8_t) 0x00FF & velocidad_0_to_1023;
uint8_t velocidad_H = (uint8_t) ((0x00FF & (velocidad_0_to_1023 >> 8)) | direccion);
velocidades[0] = 0x20;
velocidades[1] = velocidad_L;
velocidades[2] = velocidad_H;
return velocidades;
}
Tienes que tener en cuenta que el dato devuelto no será destruido nunca durante la ejecución del programa, porque como se ha reservado explícitamente, se debe liberar explícitamente cuando ya no necesites usar más el vector.
int main(int argc, char * argv[]) {
uint16_t velocidad_0_to_1023;
uint8_t direccion;
// Obtenemos los datos que vamos a pasar a la función
uint8_t * velocidades = velocidad(velocidad_0_to_1023, direccion);
// Trabajas con el vector que está en memoria montón
free(velocidades);
return 0;
}
En el ejemplo, se obtiene un vector utilizando memoria dinámica al llamar a la función velocidad
, lo utilizas para cálculos, lo imprimes, lo que quieras hacer; y más tarde lo liberas mediante la función free
.
Tercera versión
Como sugiere MrDave1999, una tercera opción es pasar la dirección de un vector ya existente a la función, de tal manera que puedes no usar memoria dinámica y no hace falta envolver el vector en una estructura.
void velocidad(uint16_t velocidad_0_to_1023, uint8_t direccion, uint8_t * velocidades)
{
if (velocidad_0_to_1023 > 1023) velocidad_0_to_1023 = 1023;
uint8_t velocidad_L = (uint8_t) 0x00FF & velocidad_0_to_1023;
uint8_t velocidad_H = (uint8_t) ((0x00FF & (velocidad_0_to_1023 >> 8)) | direccion);
velocidades[0] = 0x20;
velocidades[1] = velocidad_L;
velocidades[2] = velocidad_H;
}
int main(int argc, char * argv[]) {
uint16_t velocidad_0_to_1023;
uint8_t direccion;
// Obtenemos los datos que vamos a pasar a la función
uint8_t destino[3];
velocidad(velocidad_0_to_1023, direccion, destino);
// Trabajas con el vector que está en la pila del main
return 0;
}
Una nota final, a raíz de la última duda planteada como comentario.
Cualquier variable local a una función, es destruida/liberada al terminar esta función. Por tanto, una manera de guardar los datos que se generan en una función es devolver una copia, como se hace cuando invocas return variable
, para cualquier variable de tipo primitivo o estructura.
Otra forma es crear la variable fuera, y como en este caso, pasar la dirección de memoria a la función, para que la función la rellene con datos.
Además, creo que tienes una confusión con respecto a crear vectores o variables en general. Y es que al declarar una variable no hace falta inicializarla.
Si declaras lo siguiente:
int x;
int vector[10];
Solo por el mero hecho de declarar una variable de tipo primitivo, como x
, se reserva memoria para dicha variable. Y cuando declaras un vector de tamaño fijo, como vector[10]
, también se reserva espacio suficiente para tantos elementos del tipo que hayas indicado. En ambos casos no hace falta dar valors iniciales a las variables.
Sin embargo, es muy importante que controles las variables y cuando van a recibir información, porque C no inicializa las variables a ningún valor. Es decir, se asigna una zona de memoria para esa variable, y el valor que contiene puede ser cualquier número. Por ejemplo, si otra aplicación usó esa dirección, y al terminar de ejecutarse se te asigna esa misma dirección para alguna variable, tu variable inicialmente puede contener el valor que tenía por el uso de la aplicación interior.
A esto, comunmente, nos solemos referir como valores basura.
Por tanto, al declarar el vector de velocidades en el main
, se reserva espacio para 3 datos, y estos contienen valores basura cualesquiera. A continuación se pasa la dirección de memoria donde empieza el vector para que la función rellene con información válida el vector, sobre escribiendo los valores basura.
Finalmente, por esto de los valores basura, es buena práctica dar una valor inicial a tus variables, menos en los casos donde sepas que las vas a iniciar más adelante y no vas a usar su valor entre su declaración y su rellenado.