Go Down

Topic: Problema dichiarazione Variabili Globali/Locali nel progetto e nelle Librerie  (Read 470 times) previous topic - next topic

MizzardNet

Salve a tutti,

ho due problemi legati alla dichiarazione delle variabili globali e locali che spiegherò in modo separato perchè uno è implementato in un normale progetto e l'altro all'interno della libreria.

PROBLEMA: Dichiarazione variabile locale una sola volta in una funzione (Void o Restituente)

Ho già trovato un post dove si citava tale argomento ed è uscito fuori l'istruzione "static" e pensavo mi avesse risolto il problema ma invece no.

Il problema noto, che alla fine dei conti è lo stesso per le dichiarazioni di variabili nelle librerie,
sono le VARIABILI IN ARRAY dove l'indice se non è costante ma modificabile tramite parametri mi genera errori di compilazione.

Per rendere le cose ancora più capibili scrivo un esempio:

--------------------------------------------------------------------------

void Funzione_1(byte Valore, byte Numero_Valori, byte Indice){

byte Valori[Numero_Valori];

Valori[Indice] = Valore;

Serial.write(Valori[Indice]);

}

--------------------------------------------------------------------------

PS: mi scuso dell'inutilità di tale funzione ma è a solo scopo dimostrativo.

Nella funzione soprastante essa genera un'array di byte tanti quanti impostati da un parametro che è sulla funzione stessa e poi si passa alla parte di codice funzionale dove l'array in base all'indice riceve il dato in ingresso che poi a sua volta viene inviato tramite seriale.

Il PROBLEMA sta nel fatto che la riga di codice "byte Valori[Numero_Valori];" viene eseguita ogni volta che viene richiamata la funzione e io desidero dichiararla una sola volta all'interno di quel blocco.

L'utilità che desidero sta nel fatto di poter usare funzioni come questa, senza dover dichiarare variabili globali specifiche in altri progetti ma semplicemente copiando ed incollando ed modificando i parametri in ingresso senza dovermi preoccuparmi del codice interno con l'IMPORTANZA che nel blocco le variabili dichiarate non perdano il loro valore perchè ho creato funzioni dove se la variabile funge da contatore deve memorizzare il valore sempre anche dopo molti richiami tenendo sempre conto che si trattino di array poichè se fosse una sola variabile potrei usare la funzione "static" ma con l'array modificabile da parametro non me lo compila.

Infine questo dubbio va anche nella creazione di librerie dove non trovo un modo di rendere un'array interno con l'indice modificabile dall'utente.

Ho cercato di capire bene la libreria dello ShiftRegister 74HC595 dove imposti il numero di integrati ed in automatico vengono generate il giusto numero di variabili richieste.
All'interno di tale libreria ho trovato la funzione "memset()", "sizeoff()" e "malloc()" e dai commenti è come se fossero tali istruzioni a generare la quantità di variabili voluta.

Allego il progetto dove viene usata la libreria per il 74hC595 così potete dare un'occhiata.

Spero che esista un modo per far si da generare la dimensione di un array all'interno di una libreria e dichiarare variabili locali una sola volta all'interno di funzioni (sempre con array).

PS: l'istruzione "static" l'ho già provata e non funziona con array ad indici dinamici modificabili, per lei deve essere tutto costante a meno che non esista un metodo apposito.

Standardoil

Controlla su un buon libro di 'c',
Comunque malloc riserva uno spazio di memoria
La dimensione la devi calcolare tu, casomai con sizeof
La malloc restituisce un puntatore a void
Che tu casterai a puntatore al tipo che ti Serve
il puntatore così  ottenuto lo potrai restituire e usare come un nuovo array
Ma tutto questo è 'male' su un Arduino, non ha abbastanza 'muscoli' per gestire questa cosa senza problemi
Giusto per cronaca questa cosa si chiama 'allocazione dinamica della memoria'
Se non sei esperto (ma molto) in 'C', lasciala stare....
Prima legge di Nelson (che sono io): Se vuoi il mio aiuto dimostrami almeno che hai letto il nostro "aiutateCi ad aiutarVi"

Non bado a studenti, che copino altrove

Tu hai problema-Io ti domando-Tu non mi rispondi: vuol dire che non ti serve più

torn24

Penso che quello che vorresti fare sia sbagliato come modo di operare!
Una funzione fa qualcosa in base ai argomenti che riceve
Una funzione che esegue un compito e che sia riutilizzabile, riceverà un array come parametro e lavorerà su quello. L'array inviato non deve necessariamente essere dichiarato globalmente, potrebbe essere locale a una funzione e inviato a un altra.



Code: [Select]



void miaFunzione(){

    int ArrayLocale[5];

    funzioneGestioneArray(ArrayLocale);

}


void funzioneGestioneArray(int* A){

      A[1]=valore;

 }


Standardoil

Non hai torto,
In effetti se il problema è di dichiarare un array  di dimensione conosciuta a runtime e non a compiletime basta dichiararlo locale alla loop e non uscire mai dalla prima chiamata della loop
Una cosa del genere
Code: [Select]

char * questa;
//variabile puntatore dichiarata ma non inizializzata
.
.
setup()
.
.



loop()
{
   while(1)
   {
      char * quella[analogRead(A0)];
      // che è Chiaramente un VLA
E non uscendo mai dalla prima chiamata della loop non "Muore" mai
Il problema è renderlo globale

questa=quella;

//adesso il puntatore "questa"
Che è globale
Si può usare come se fosse
L'array "quella" che è invece locale
Ma non muore mai come se fosse globale

// qui ci mettiamo tutto il resto della loop

   }
}


per lo OP invece:
Urge che tu metta i tag code
Te pensa che io li ho usati pur se da furbofono...
Prima legge di Nelson (che sono io): Se vuoi il mio aiuto dimostrami almeno che hai letto il nostro "aiutateCi ad aiutarVi"

Non bado a studenti, che copino altrove

Tu hai problema-Io ti domando-Tu non mi rispondi: vuol dire che non ti serve più

Silente

A livello teorico avere uno o più array non globali di dimensione modificabile dall'utente é possibile. Tutto gioca sul fatto che alla fine di una funzione lonspazio occupato dalle sue variabili é solo dichiarato libero, ma NON liberato. Gli array a dimensione modificabile sono quindi possibili in questo modo:
1) li crei come array normali in una funzione apposta, che conosce le loro lunghezze (e questo lo puoi fare senza problemi)
2) la funzione fa cose (riempirli, leggerli, altro...) e poi termina restituendo il puntatore al primo elento del primo array
3)salvi quel puntatore innuna variabile globale apposita, che può essere usato come array (ma NON é un array, lo spazio che occupa é considerato LIBERO)
4) da quando esci dalla funzione in poi non deve più essere dichiarata NESSUNA variabile, ne da te ne da altre funzioni. Esse amdrebbero infatti a scriversi al posto del contenuto di quegli array (puoi usare tranquillamente variabili già dichiarate).
5) richiami la funzione tutte le volte che vuoi per modificare il contenuto e la dimensione degli array

Per evitare restrizioni così assolute puoi dichiarare nella funzione creatrice un array di lunghezza che si ritiene sufficiente PRIMA degli altri senza usarlo. Quello spazio sarà utilizzabile per altre variabili locali, una volta usciti dalla funzione.
Come hai capito tutto questo ha il grande difetto dell'estrema fragilità, ma é così che farei una cosa che non si può fare (a meno di non essere dei geni di programmazione sadici)
Dove va un numero va una variabile, una funzione e/o  un test.
Per ottenere devi spiegare

Strumenti/Formattazione automatica fino alla morte!
Cristianesimo:bibbia='C':K&R

MizzardNet

Ma almeno qualcuno conosce un codice da implementare in una libreria dove per esempio la variabile
"DATA[]" ha l'indice modificabile all'uscita nella istanza ?

ES:

#include "FLIPFLOP.h"

FLIPFLOP FF_1(NumOuts, Data, Clock);

Dove il parametro "NumOuts" fa a definire l'array interno.

Esiste un metodo o no ? Ci sono esempi ?
Qualcuno sa come fare o è una cosa COMPLICATISSIMA che solo a me mi serve ?

Penso sia uno dei quesiti più difficili ma altrettanto efficienti.

Grazie per l'eventuale aiuto...

Standardoil

Prima legge di Nelson (che sono io): Se vuoi il mio aiuto dimostrami almeno che hai letto il nostro "aiutateCi ad aiutarVi"

Non bado a studenti, che copino altrove

Tu hai problema-Io ti domando-Tu non mi rispondi: vuol dire che non ti serve più

paolo311

Dove il parametro "NumOuts" fa a definire l'array interno.
Perdonami, ma "interno" rispetto a cosa?

Se intendi dichiarare un array "interno" ad una funzione e aspettarti che mantenga lo stato anche fuori, devi cambiare la logica perchè questo non accadrà mai, una volta usciti dal blocco le variabili dichiarate in esso non sono più referenziabili a meno di non usare i trucchi che ti hanno già indicato (appoggi il riferimento su un alias globlale o con scope più ampio e poi lo vai a riprendere, etc..)

Oppure cambi la logica, la funzione può diventare un metodo di classe, all'interno della quale c'è un membro privato che è l'array, in questo modo l'array resta nascosto ma mantiene lo stato all'uscita dal metodo

Una cosa così:
Code: [Select]

#include <Arduino.h>

class MyDumbArrayOfBytes
{
private:
    byte* internalArray;
    int size;

public:
    MyDumbArrayOfBytes(int _size);
    ~MyDumbArrayOfBytes();

    void Set(int _index, byte _value);
    byte Get(int _index);
    int Size();
};

MyDumbArrayOfBytes::MyDumbArrayOfBytes(int _size)
{
    internalArray = new byte[_size];
    size = _size;
}

MyDumbArrayOfBytes::~MyDumbArrayOfBytes()
{ }

void MyDumbArrayOfBytes::Set(int _index, byte _value)
{
    internalArray[_index] = _value;
}

byte MyDumbArrayOfBytes::Get(int _index)
{
    return internalArray[_index];
}

int MyDumbArrayOfBytes::Size()
{
    return size;
}


Che si può usare così:
Code: [Select]

#include <Arduino.h>
#include <MyDumbArrayOfBytes.h>

void setup()
{
  Serial.begin(9600);

  MyDumbArrayOfBytes mdaob(analogRead(A0));

  Serial.println(mdaob.Size());
  for (int i = 0; i < mdaob.Size(); i++)
  {
    Serial.println(mdaob.Get(i));
  }
}


Per adattarlo alle tue necessità devi effettuare una scelta, o separi il metodo che alloca l'array da quello che lo utilizza e in questo caso la classe va più o meno bene così, oppure se vuoi mantenere la logica attuale devi dotarti di flagettino che setti a true alla prima inizializzazione e lo testi sulle successive in modo da non ripetere l'allocazione dell'array.

Ciao

Standardoil

Tu saresti (leggo dalla tua presentazione) un po' arrugginito?
Quando riprenderai appieno cosa farai?
Riscriverai lo IDE per farlo girare su una MEGA?

Sia chiaro: il mio è un complimento, un grosso complimento
Prima legge di Nelson (che sono io): Se vuoi il mio aiuto dimostrami almeno che hai letto il nostro "aiutateCi ad aiutarVi"

Non bado a studenti, che copino altrove

Tu hai problema-Io ti domando-Tu non mi rispondi: vuol dire che non ti serve più

Silente

Comunque non capisco il bisogno di avere array di lunghezza variabile ma i cui valori si mantengano. Sapendo che tu scrivi il programma la successione degli eventi é questa:
0) ti serve un array per delle ragioni (farci cose)
1) lo crei
3) lo riempi
4) ci fai cose
5) l'array non ti é più necessario, quindi perché tenerlo?
6) torna a 0

E se la successione é questa non ci sono problemi, basta creare un normale array in una funzione e usarlo prima che la funzione finisca, e questo lo sai fare
Dove va un numero va una variabile, una funzione e/o  un test.
Per ottenere devi spiegare

Strumenti/Formattazione automatica fino alla morte!
Cristianesimo:bibbia='C':K&R

Standardoil

Beh, una qualche utilità la avrebbe, comunque
Ma con arduino diventa duretta...
Per paolo311
Se si ridefinissero (overloading) anche gli operatori [ ] un oggetto di una tale classe sarebbe indistinguibile nell'uso da un array, o sbaglio?
Prima legge di Nelson (che sono io): Se vuoi il mio aiuto dimostrami almeno che hai letto il nostro "aiutateCi ad aiutarVi"

Non bado a studenti, che copino altrove

Tu hai problema-Io ti domando-Tu non mi rispondi: vuol dire che non ti serve più

paolo311

Se si ridefinissero (overloading) anche gli operatori [ ] un oggetto di una tale classe sarebbe indistinguibile nell'uso da un array, o sbaglio?
Diciamo che ci assomiglia.. :)

Però tieni presente che una cosa del genere ha senso solo se hai dei metodi a corredo dell'array che manipolano in modo particolare i dati memorizzati, oppure se vuoi vincolare in qualche modo l'accesso diretto ai dati.

Tant'è che l'allocazione con il new funziona anche a "secco"..

Code: [Select]

  byte* mdaob;
  int size = analogRead(A0);
  mdaob = new byte[size];
 

  Serial.println(size);
  for (int i = 0; i < size; i++)
  {
    Serial.println(mdaob[i]);
  }


PS. Grazie del complimento, però per me riprendere a sviluppare in c\c++ è come guidare dopo tanti anni una macchia senza servosterzo.

Grazie
Ciao

Standardoil

E così  ricadiamo nella mia prima risposta, dove però  io usavo una alloc () non conoscendo io bene il c++
Si, in effetti le soluzioni quelle sono...
Prima legge di Nelson (che sono io): Se vuoi il mio aiuto dimostrami almeno che hai letto il nostro "aiutateCi ad aiutarVi"

Non bado a studenti, che copino altrove

Tu hai problema-Io ti domando-Tu non mi rispondi: vuol dire che non ti serve più

MizzardNet

Ma tu hai letto le mie risposte?
Ho letto tutto ma non trovato una soluzione alle tue due risposte.

Tu saresti (leggo dalla tua presentazione) un po' arrugginito?
Quando riprenderai appieno cosa farai?
Riscriverai lo IDE per farlo girare su una MEGA?

Sono arrugginito su questo argomento in tale post che io ho pubblicato.

Diciamo che il progetto è di creare una libreria che implementi un mio protocollo seriale con real time interno e vorrei avere come parametro il numero di byte massimi che il buffer di ricezione può contenere ed il numero di byte massimi che la funzione interna del Real Time mi invia al colpo in base ad un tempo prefissato.

NB: il blocco Real Time è una funzione a parte e serve solo per aggiornare i dati per una sicurezza di stabilità quindi può essere anche disattivato e l'invio e la recezione funzionano tranquillamente.

E visto che sono programmatore di PLC dove si lavora a linguaggio grafico a blocchi mi viene naturale generare delle librerie come se fossero i blocchi funzionali come le Macro nel PLC o più semplicemente funzioni e quindi desidero farlo bene in modo robusto per poi anche pubblicarle con tutta la documentazione :)

Io di media uso microcontrollori STM32 principalmente STM32F103C8T6 ed la versione cicciona STM32F407ZGT6 per motivi di presentazioni elevate (overclok, tanti GPIO e potenza da 32bit).

Ma come regola cerco sempre di fare girare il codice su un ATMega328P per esser sicuro che anche un micro ad 8 bit "semplice" lo supporti poiché noto di più gli errori come calcoli sulla latenza ecc (tramite l'oscilloscopio) per poi usarlo su Micro un po' più seri...


Perdonami, ma "interno" rispetto a cosa?

Se intendi dichiarare un array "interno" ad una funzione e aspettarti che mantenga lo stato anche fuori, devi cambiare la logica perchè questo non accadrà mai, una volta usciti dal blocco le variabili dichiarate in esso non sono più referenziabili a meno di non usare i trucchi che ti hanno già indicato (appoggi il riferimento su un alias globlale o con scope più ampio e poi lo vai a riprendere, etc..)

Oppure cambi la logica, la funzione può diventare un metodo di classe, all'interno della quale c'è un membro privato che è l'array, in questo modo l'array resta nascosto ma mantiene lo stato all'uscita dal metodo

Una cosa così:
Code: [Select]

#include <Arduino.h>

class MyDumbArrayOfBytes
{
private:
    byte* internalArray;
    int size;

public:
    MyDumbArrayOfBytes(int _size);
    ~MyDumbArrayOfBytes();

    void Set(int _index, byte _value);
    byte Get(int _index);
    int Size();
};

MyDumbArrayOfBytes::MyDumbArrayOfBytes(int _size)
{
    internalArray = new byte[_size];
    size = _size;
}

MyDumbArrayOfBytes::~MyDumbArrayOfBytes()
{ }

void MyDumbArrayOfBytes::Set(int _index, byte _value)
{
    internalArray[_index] = _value;
}

byte MyDumbArrayOfBytes::Get(int _index)
{
    return internalArray[_index];
}

int MyDumbArrayOfBytes::Size()
{
    return size;
}


Che si può usare così:
Code: [Select]

#include <Arduino.h>
#include <MyDumbArrayOfBytes.h>

void setup()
{
  Serial.begin(9600);

  MyDumbArrayOfBytes mdaob(analogRead(A0));

  Serial.println(mdaob.Size());
  for (int i = 0; i < mdaob.Size(); i++)
  {
    Serial.println(mdaob.Get(i));
  }
}


Per adattarlo alle tue necessità devi effettuare una scelta, o separi il metodo che alloca l'array da quello che lo utilizza e in questo caso la classe va più o meno bene così, oppure se vuoi mantenere la logica attuale devi dotarti di flagettino che setti a true alla prima inizializzazione e lo testi sulle successive in modo da non ripetere l'allocazione dell'array.

Ciao
Intendo interna alla Libreria che desidero creare.

IN SPECIFICO:

Vorrei fare una libreria dove adotta un protocollo seriale per mandare dati binari ovvero singoli bit e quindi tale libreria deve avere come parametri il numero di bit in ingresso in modo da "collegarlo" direttamente ad un PIN della scheda dichiarato ingresso ed il numero di bit d'uscita usati per comandare direttamente un PIN digitale. (Può valere anche con i Bytes la logica per il calcolo dei Byte tramite i Bit o viceversa la so fare).

La libreria fa praticamente tutto; invio e recezione dati tramite bit che gestisco io dal programma  nel loop. Ci sono ovviamente parametri d'indirizzo, marcatori ecc ma la cosa che non so fare è rendere flessibile la libreria e far scegliere all'utente quanti sengali digitali (bit) inviare e quanti ricevere.

(Sto leggendo e cercando di apprendere il più possibile dalle risposte e noto che ci sono molte cose sul codice mai viste)

Sempre per Paolo311 il tuo codice sembra proprio letto velocemente a quello che pensavo con le corrispettive funzioni di writer e read ma non capisco da dove sia stato tirato fuori il nome "mdaob" e poi il codice è tutto intero o è diviso uno nel file.h e le funzioni nel file.ccp ? Perché vedo le dichiarazioni delle variabili e poi subito sotto le funzioni di Get() Set() ecc ecc XD sono confuso di librerie non ne ho fatte molte ma ho sempre usato due file uno .h e uno .ccp e avvolte vedendo librerie di altri hanno tanti file .h penso o tanti file ccp. Sono confuso XD

Se uno sa o ha usato librerie che implementano tale metodo le condivida così forse leggendo il loro interno capsico bho XD

Standardoil

non credo che tu abbia letto le mie risposte, per due ragioni:
1) non hai nemmeno capito quando scrivo a te e quando no
2) ci sono scritte, nelle mie risposte, tutte e due le "sole" due soluzioni possibili, di una poi ti è stata anche indicata la versione c++
inoltre:
3) tu hai già trovato e letto le librerie che fanno quello che cerchi, hai esattamente trovato quello che ti indicavo io, ma non ne hai fatto niente
e quindi? cosa aspetti adesso a prenderti un buon libro e a studiare quello che ti abbiamo detto?
credimi: è escluso che, ad esempio, io ti scriva la tua "funzione magica", fai molto prima a studiare
per il resto: buon viaggio
Prima legge di Nelson (che sono io): Se vuoi il mio aiuto dimostrami almeno che hai letto il nostro "aiutateCi ad aiutarVi"

Non bado a studenti, che copino altrove

Tu hai problema-Io ti domando-Tu non mi rispondi: vuol dire che non ti serve più

Go Up