Dubbio Puntatori-Variabili Globali

Buongiorno a tutti!
Modificando il codice di uno sketch per gestire 3 linee di luci mi sono imbattuto nell'ancestrale dubbio sull'utilizzo dei puntatori.
Inizialmente globalmente venivano dichiarate 3 variabile distinte per le 3 linee di luci : (es. On1,On2,On3);
Per rendere tutto intuitivo e pratico ho deciso di sostituirle con degli array (es. On[2],Off[2] ...)

Il problema è che sostituendo i parametri nelle funzioni sembra che non sia possibile modificare le variabili globali negli array...
Prima le variabili venivano passate cosi...

byte On1, On2 On3;


void loop(){

funzione (&On1,&On2,&On3);
}

funzione (byte *O1, byte *O2, byte *O3){
O1=1;
O2=2;
O3=3;
}

Mente volevo modificarle in

byte On[2];

void loop(){
funzione (0);
}

funzione (byte n){
On[n]=1;
On[n+1]=2;
On[n+2]=3;
}

Tutti e due i metodi dovrebbero modificare le variabili a livello globale giusto?

è la stessa cosa che scrivere ?

byte On1, On2 On3;


void loop(){

funzione (On1,On2,On3);
}

funzione (byte &O1, byte &O2, byte &O3){
O1=1;
O2=2;
O3=3;
}

Spero di essermi spiegato bene.. perche nel caso degli array sembra non funzioni come dovrebbe: non modifica la variabile globalmente..
Buona giornata a tutti!

esempio N°1 errato:

funzione (byte *O1, byte *O2, byte *O3){
O1=1;
O2=2;
O3=3;
}

così stai cambiando gli INDIRIZZI dei puntatori O1, O2 e O3 con i valori 1, 2 e 3. Tu vuoi cambiare i valori quindi ci va il * davanti (notare le parentesi, l'= ha prioristà rispetto a * !):

funzione (byte *O1, byte *O2, byte *O3){
(*O1)=1;
(*O2)=2;
(*O3)=3;
}

oppure, visto che array e puntatori sono la stessa cosa, puoi considerare il puntatore ad una variabile come un array di dimensione 1:

funzione (byte *O1, byte *O2, byte *O3){
O1[0]=1;
O2[0]=2;
O3[0]=3;
}

Quindi dire (*puntatore)=x o puntatore[0] = x è la stessa cosa!

invece il seguente che io sappia non ha senso, non dovrebbe nemmeno compilare:

funzione (byte &O1, byte &O2, byte &O3){

Per prima cosa, essendo globali non importa che le passi come riferimento, funziona anche così:

byte On1, On2, On3;

void loop() {
  funzione ();
}

funzione () {
  O1=1;
  O2=2;
  O3=3;
}

Seconda cosa: quando definisci un array dentro le quadre devi mettere il numero di elementi, quindi variabile[3] sarà formata dagli elementi variabile[0], variabile[1], variabile[2]. Attenzione, nessuno ti vieta di andare a leggere/scrivere variabile[3] ma non sai cosa c'è, potresti fare danno (a livello software, chiaramente).

Terza cosa: il c++ tratta gli array come puntatori, quindi se crei un array variabile[3] lui ti crea un puntatore al primo elemento della lista. Per trovare un elemento della lista lui fa questa cosa:
variabile[2] = *(variabile+2)
A questo punto ti svelo un segreto del c++:
variabile[2] = *(variabile+2) = *(2+variabile) = 2[variabile]

E' un "baco" intrinseco del c++ per come tratta gli array... :wink:

Ciao,

funzione (byte &O1, byte &O2, byte &O3)

Cosa intenderesti fare con questa funzione ?
Se la tua idea era accettare dei parametri di tipo puntatore avresti dovuto mettere

funzione A(byte * O1, byte * O2, byte * O3)
{
// corpo della funzione
}

e chiamando quella funzione invece avresti dovuto mettere

A(&On1,&On2,&On3);

Perchè tutto ciò?

Semplice, nella firma della funzione specifici che il tipo di dato che passi, non èun byte, ma un puntatore ad un valore di quel tipo, e lo fai mettendo il simbolo *

Quando invece vai a richiamare quella funzione, devi indicare che non vuoi passare il valore della tua variabile On1,On2,On3 , ma bensi l'indirizzo di memoria in cui questa è stata allocata, pertanto devi mettere il simbolo &

In parole povere :

funzione A(byte * O1, byte * O2, byte * O3)
{
// corpo della funzione
}

significa che la tua funzione si aspetta di trovare 3 puntatori a byte come parametri (e attenzione poi a come li usi all'interno della funzione stessa!!)

e

A(&On1,&on2,&On3)

equivale a : Chiama la funzione A passando L'INDIRIZZO DI MEMORIA di On1,L'INDIRIZZO DI MEMORIA di On2,L'INDIRIZZO DI MEMORIA di On3

Spero di averti chiarito.

Comunque il problema che lamenti per l'array globale non esiste, sicuramente hai fatto confusione .
Se passi alla funzione il puntatore corretto all'array, riesci tranquillamente a variarne il suo contenuto da qualsiasi parte del codice.

Janos:
Terza cosa: il c++ tratta gli array come puntatori, quindi se crei un array variabile[3] lui ti crea un puntatore al primo elemento della lista. Per trovare un elemento della lista lui fa questa cosa:
variabile[2] = *(variabile+2)
A questo punto ti svelo un segreto del c++:
variabile[2] = *(variabile+2) = *(2+variabile) = 2[variabile]

E' un "baco" intrinseco del c++ per come tratta gli array... :wink:

non ho capito la storia del baco, puoi spiegare meglio?

comuqnue che io sappia:
variabile[2] = (variabile+2sizeof(tipoDell'array))

dove sizeof è la dimensione in byte della variabile float (4 byte nei sistemi 32bit, 8byte nei 64bit, ecco perchè inviare un float grezzo da arduino ad un PC 64bit può essere più problematico del previsto :slight_smile: )

importante perchè dando ad un array void non puoi usare [] (alcune implementazioni puoi e ti muovi di un byte), o puoi leggere un array di float byte per byte invece che float per float cambiando semplicemente il tipo di puntatore all'array...

per spiegarmi:

float n = 12.3;
byte *p;
p = &n; //ora P punta all'area di memoria di n
for (int i=0; i<sizeof(float); i++){
   Serial.print("byte: ");
   Serial.print(i);
   Serial.print(" = ");
   Serial.println(p[i], BIN);
}

lesto:
non ho capito la storia del baco, puoi spiegare meglio?

Prova a eseguire questo codice:

int variabile[3] = {55, 46, 19};

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

void loop() {
  Serial.println(variabile[1],DEC);
  Serial.println(1[variabile],DEC);
  delay(1000);
}

EDIT
Ho modificato il codice, diciamo che ero stato un po troppo precipitoso nello scriverlo... =)

Serial.println(variabile[1],DEC);
  Serial.println(1[variabile],DEC);

ad occhio direi che dimostra che la chiamata sizeof() non viene fatta come dico io esplicitamente ma viene fatta all'interno dell'operatore +:

indirizzo di variabile = 100
print1 = 100+1int (cioè 2 byte) = 102
print2 = 1int(cioè 2 byte) +100 = 102

con la sizeof esplicita:
print1 = 100+ 1sizeof(int)= 102
print2 = 1 +100
sizeof(int *) = 201
(tutti i puntatori sono di 2 byte!, ma in realtà fa una sizeof di variabile... se variabile è stato dicjhiarato come array, allora ritorna il numero dei suoi elementi (3 nel nostro caso), se invece è un puntatore all'array, oppure l'array è stato passato ricevuto come parametro (quindi in realtà è stato di nascosto trasformato in puntatore) ritorna sempre la dimensione dell'area di memoria del puntatore, che è 2byte (forse 4 nei 64bit) )

Perchè non dovrebbe funzionare ? Arduino non è standard C++?

byte On1, On2 On3;
void loop()
{ funzione (On1,On2,On3);
}

funzione (byte &p1, byte &p2, byte &p3)
{ 
}

Questo è "Passaggio di parametri per riferimento"

no, arduino usa il Wiring, che è in pratica C con un pizzico di C++..
e non conoscevo questa semplificazione del C++, c'è da vedere se arduino la supporta.

Quindi il primo sistema corretto è identico al terzo

Wiring non è un linguaggio di programmazione, Arduino si programma in C++ al quale è stato aggiunto il framework Wiring, ovvero una serie di funzioni/librerie/API per semplificare la vita al programmatore. Wiring è un framework, non un linguaggio............

lesto:
e non conoscevo questa semplificazione del C++, c'è da vedere se arduino la supporta.

A quale ti riferisci? A quella dell'esempio che ho postato io?

quella della & per ricevere puntatori al posto del *

Arduino si programma in C++

Hai ragione ho controllato, la il punto è che è un C++ castrato, la prova è che negli IDE < 1.0 non esisteva nemmeno l'operatore new e delete.

lesto:
quella della & per ricevere puntatori al posto del *

Arduino si programma in C++

Hai ragione ho controllato, la il punto è che è un C++ castrato, la prova è che negli IDE < 1.0 non esisteva nemmeno l'operatore new e delete.

Questi sono stati introdotti nella versione 1.0.3, ne detti notizia io qualche settimana fa ricordi?

Ma quindi a parte l'errore che ho fatto di digitazione nella prima le 3 forme sono uguali?

byte On1, On2 On3;
void loop()
{ funzione (On1,On2,On3);
}

funzione (byte &p1, byte &p2, byte &p3)
{ 
}

Che io sappia questa forma è propria del C++ (non c'è nel C) come dice nid69ita
ho provato ad utilizzarla e "sembra" funzionare... qualcuno però mi può confermare che funzioni come dovrebbe?

sì, non ricordavo la versione precisa ed ho generalizzato.
Il punto è che bisogna fare mooolta attenzione a dire che è C++, io direi che

è in pratica C con un pizzico di C++..

Ma quindi a parte l'errore che ho fatto di digitazione nella prima le 3 forme sono uguali?

bhe la seconda usa un array che ha vantaggi e svantaggi ma in linea di massima sì, sono equivalenti

Che io sappia questa forma è propria del C++ (non c'è nel C) come dice nid69ita

esatto, se compila funziona, vai tranquillo

Quello che hai fatto è passare alla funzione dei parametri come riferimento:

Il che è tanto corretto quanto inutile. Come ti ho già detto sopra, quelle variabili sono definite globali e quindi visibili in tutto il programma. Se fai diventare il tuo codice così:

byte On1, On2 On3;
void loop()
{ funzione ();
}

funzione ()
{ 
}

Funziona alla stessa maniera. Ti consiglio di dare un'occhiata a questo link, che fra l'altro spiega anche il tempo di vita delle stesse...

http://webuser.unicas.it/tortorella/FondInf_0809/PDF/10-visibilita%20variabili.pdf

Personalmente preferisco l'uso delle variabili globali rispetto ai riferimenti, perché il firmware compilato è più leggero. La gestione dei riferimenti è una di quelle cose con cui il compilatore si barcamena più di altre.

lesto:
sì, non ricordavo la versione precisa ed ho generalizzato.
Il punto è che bisogna fare mooolta attenzione a dire che è C++, io direi che

è in pratica C con un pizzico di C++..

Ma quindi a parte l'errore che ho fatto di digitazione nella prima le 3 forme sono uguali?

bhe la seconda usa un array che ha vantaggi e svantaggi ma in linea di massima sì, sono equivalenti

Che io sappia questa forma è propria del C++ (non c'è nel C) come dice nid69ita

esatto, se compila funziona, vai tranquillo

:roll_eyes:
Dunque, io ho già scritto riguardo il linguaggio di Arduino, poi di recente Astrobeed ha confermato ciò che andavo dicendo 2 anni addietro.
Le mie intenzioni sono quelle di fissare una volta per tutto delle verità inconfutabili:
Il compilatore usato da Arduino IDE è il ben noto avr-gcc ottenuto compilando gcc per architettura AVR. gcc è in grado di compilare molti linguaggi persino java, ada, objectiveC ecc, ovviamente la libreria standard C e la stdc++ devono essere adattate per ambiente embedded, quindi per AVR è stata sviluppata la avr-libc e manca un progetto per implementare la stdc++ embedded, manca perchè la stdc++ è immensa e molte cose lato embedded sarebbero inutili o poco funzionali o poco efficienti o.... insomma non c'è la sdtc++ per embedded, c'è però qualche libreria C++ che offre alcune funzionalità offerte dalla stdc++ lato PC, visto che le funzionalità di questa libreria C++ embedded non ricalca in modo fedele la sdtc++ sarebbe controproducente chiamarla allo stesso modo, cioè libstdc++.

Arduino IDE può compilare codice sorgente .c, .cpp, .c++, .s ecc, questo significa che se il sorgente ha estensione .c viene chiamato il compilatore C e il preprocessore C. La fase finale di "linkaggio" dei moduli compilati in formato oggetto avviene richiamando g++ con l'opzione per linkare codice C++.

Il C++ usabile con arduino è completo, ci sono anche i template.
Ma allora cosa manca:
Ci sono le eccezioni disabilitate, visto che una eccezione avviene a runtime avrebbe poco senso abilitarla, se abilitata la dimensione del compilato .ihex aumenta sensa poterne trarre vantaggio.

L'operatore new è disabilitato perchè manca la libreria standard C++, allora il team Arduino ha sopperito fornendo la gestione dello heap adatta a risorse limitate tramite l'operatore new e delete specifici.

Mancando le librerie stdc++ manca anche la gestione dei thread che normalmente si appoggia ai servizi di sistema, sistema che non esiste.

Altra mancanze potrebbero esserci, ma non ho indagato circa il binding dinamico perchè è lento sul PC figuriamoci su atmega a 16MHZ e si usa solo con le classi astratte e i metodi virtuali puri.

Il team Arduino ha ufficialmente scritto una cosa che porta fuori strada l'utente, sicuramente avrà buoni motivi per averlo fatto ma queste sono le conseguenze. L'incontro tra competenti e non competenti in questo modo viene a mancare e il dialogo si guasta, non solo chi sostiene il contrario di ciò che sostiente il team arduino non è credibile e si finisce sempre a dire le stesse cose.

Sono anche convinto che in questo modo l'utonto fa più fatica ad evolversi.

Ho uno strano senso di dejavu. :stuck_out_tongue:

Ciao

MauroTec:
L'operatore new è disabilitato perchè manca la libreria standard C++, allora il team Arduino ha sopperito fornendo la gestione dello heap adatta a risorse limitate tramite l'operatore new e delete specifici.

Purtroppo mi sa che la delete non libera la memoria allocata. Ho provato questo codice:

Serial.println(FreeRam());
unsigned int *variabile = new unsigned int;
*variabie = 0;
Serial.println(FreeRam());
...
...
...
Serial.println(FreeRam());
delete variabile;
Serial.println(FreeRam());

La prima volta che passo da quelle istruzioni mi stampa

504
500
500
500

Le successive invece

500
500
500
500

MauroTec:
Il team Arduino ha ufficialmente scritto una cosa che porta fuori strada l'utente,

Che cosa?

Sulla homepage di arduino c'è scritto così:

...
The microcontroller on the board is programmed using the Arduino programming language (based on Wiring) and the Arduino development environment (based on Processing).
...