Contatore byte utilizzati sketch aumenta poco...

Salve Ragazzi,

Sto riscontrando una stranezza, mi è capitato gia per vari sketch lunghi, che il contatore byte utilizzati al momento della VERIFICA o COMPILAZIONE salga di poco o addirittura non si muova affatto! per esempio ho uno sketch che ha circa 900 linee (di cui, magari 80% codice) con tantissimi array mono e multidimensionali e il compilatore mi dice che sto usando "solo" 3-4kb ?? se faccio un programmini qualsiasi corto, sono già a quelle cifre di byte!

Da premettere che ho usato IDE 1.02, ma caricati dentro 1.05 non cambia.

Altra cosa curiosa, tenendo aperti due sketch diversi, se mando a verificare i due sketch in contemporanea, ottengo swappati i byte consumati!! ovvero, i byte occupati dello sketch A vengono indicati nello sketch B e quelli del B nello sketch A !!! Pazzesco!! Molto curioso!!

Quei K di memoria sono di programma (Flash) non per i dati (SRAM). Se crei un array di 200 byte occupi memoria SRAM non di Flash del programma.

Si lo so che sono di flash, quello che mi è strano è che se faccio un semplice sketch magari con 20-30 dichiarazioi variabili, magari qualche calcolo e alcuni Serial.print(), il contatore mi aumenta ogni qualvolta aggiungo codice, invece a questi miei sketch lunghi il contatore segna sempre pochi byte ed avvolte addirittura non si muove per nulla, anche aggiungendo 10 nuove dichiarazioni variabili (byte o int che siano), ma io mi aspetterei di aver consumato almeno 10-15kb di Flash disponibile!

Non posso postare lo sketch, ma per esempio, uno di questi ha dichiarate 140 variabili/constati (per lo più byte e qualche int e qualche unsigned int e qualche float) + dichiarato ed inizializzato 37 array per un totale di 900byte circa e tante righe di codice per calcoli vari, analogRead (tutti i canali ADC usati), e almeno 10 Serial.print() !! penso che tutto questo codice occupi più di 3870 bytes!!

Ho fatto questo sketch di esempio:

// Esempio spazio FLASH occupato dopo compilazione:
//
//Dichiarare 110 variabili int senza nessun "Serial.begin(9600)" occupa solo 466 bytes, 
//come quanti ne occupa dichiarendone 10 variabili, mettendo Solo il Serial.begin(9600)
//il conteggio sale a 1744 bytes e mettendo un solo Serial.print(var1) sale a 2248 bytes.
//mettendoci anche 30 array dichiarate e inizializzate, il conteggio rimane 2248 bytes!!


int var1;
int var2;
int var3;
int var4;
int var5;
int var6;
int var7;
int var8;
int var9;
int var10;
int var11;
int var12;
int var13;
int var14;
int var15;
int var16;
int var17;
int var18;
int var19;
int var20;
int var21;
int var22;
int var23;
int var24;
int var25;
int var26;
int var27;
int var28;
int var29;
int var30;
int var31;
int var32;
int var33;
int var34;
int var35;
int var36;
int var37;
int var38;
int var39;
int var40;
int var41;
int var42;
int var43;
int var44;
int var45;
int var46;
int var47;
int var48;
int var49;
int var50;
int var51;
int var52;
int var53;
int var54;
int var55;
int var56;
int var57;
int var58;
int var59;
int var60;
int var61;
int var62;
int var63;
int var64;
int var65;
int var66;
int var67;
int var68;
int var69;
int var70;
int var71;
int var72;
int var73;
int var74;
int var75;
int var76;
int var77;
int var78;
int var79;
int var80;
int var81;
int var82;
int var83;
int var84;
int var85;
int var86;
int var87;
int var88;
int var89;
int var90;
int var91;
int var92;
int var93;
int var94;
int var95;
int var96;
int var97;
int var98;
int var99;
int var100;
int var101;
int var102;
int var103;
int var104;
int var105;
int var106;
int var107;
int var108;
int var109;
int var110;

//se togliamo queste array mono e bidimensionali dichiarate ed inizializzate il conteggio Flash usata è sempre lo stesso!


int Array1[10] = {1,2,3,4,5,6,7,8,9};
int Array2[10] = {2,3,4,5,6,7,8,9,10};
int Array3[10] = {3,4,5,6,7,8,9,10,11};
int Array4[10] = {4,5,6,7,8,9,10,11,12};
int Array5[10] = {3,4,5,6,7,8,9,10,11};
int Array6[10] = {11,21,31,41,51,61,71,18,19};
int Array7[10] = {11,22,33,44,55,66,77,88,98};
int Array8[10] = {10,20,30,40,50,60,70,80,90};
int Array9[10] = {40,50,60,70,80,90,100,110,120};
int Array10[10] = {15,25,35,45,55,65,75,85,95};
int Array11[10] = {15,62,73,84,59,60,72,82,29};
int Array12[10] = {122,22,322,234,215,236,457,238,219};
int Array13[10] = {112,223,334,454,565,756,127,128,19};
int Array14[10] = {124,455,665,732,821,912,110,111,222};
int Array15[10] = {6,26,36,45,55,67,76,84,93};
int Array16[10] = {12,23,32,43,658,96,207,18,129};
int Array17[10] = {122,1232,223,134,6545,326,127,128,4349};
int Array18[10] = {1876,6562,453,24,255,526,2547,248,559};
int Array19[10] = {123,532,345,4,455,456,337,328,119};
int Array20[10] = {341,452,453,564,675,786,557,458,349};

byte Array22[16][8]=                                     
        { {40,45,46,48,49,50,52,53},
          {42,45,47,49,50,51,53,54},
          {43,46,48,49,51,52,54,55},
          {44,47,49,50,52,53,55,56},
          {45,48,50,52,55,56,57,58},
          {46,49,51,53,55,57,58,59},
          {47,50,52,55,56,58,60,62},
          {48,52,53,55,57,59,62,63},
          {49,53,54,56,58,60,63,64},
          {50,54,55,56,57,61,64,65},
          {51,55,56,57,58,62,65,66},
          {52,56,57,58,59,60,61,62},
          {53,57,58,59,60,61,62,63},
          {54,55,56,60,61,62,63,64},
          {55,56,57,61,62,63,64,65},
          {56,57,58,62,63,64,65,66} };

byte Array23[16][8]=                                     
        { {40,45,46,48,49,50,52,53},
          {42,45,47,49,50,51,53,54},
          {43,46,48,49,51,52,54,55},
          {44,47,49,50,52,53,55,56},
          {45,48,50,52,55,56,57,58},
          {46,49,51,53,55,57,58,59},
          {47,50,52,55,56,58,60,62},
          {48,52,53,55,57,59,62,63},
          {49,53,54,56,58,60,63,64},
          {50,54,55,56,57,61,64,65},
          {51,55,56,57,58,62,65,66},
          {52,56,57,58,59,60,61,62},
          {53,57,58,59,60,61,62,63},
          {54,55,56,60,61,62,63,64},
          {55,56,57,61,62,63,64,65},
          {56,57,58,62,63,64,65,66} };

byte Array24[16][8]=                                     
        { {40,45,46,48,49,50,52,53},
          {42,45,47,49,50,51,53,54},
          {43,46,48,49,51,52,54,55},
          {44,47,49,50,52,53,55,56},
          {45,48,50,52,55,56,57,58},
          {46,49,51,53,55,57,58,59},
          {47,50,52,55,56,58,60,62},
          {48,52,53,55,57,59,62,63},
          {49,53,54,56,58,60,63,64},
          {50,54,55,56,57,61,64,65},
          {51,55,56,57,58,62,65,66},
          {52,56,57,58,59,60,61,62},
          {53,57,58,59,60,61,62,63},
          {54,55,56,60,61,62,63,64},
          {55,56,57,61,62,63,64,65},
          {56,57,58,62,63,64,65,66} };

byte Array25[16][8]=                                     
        { {40,45,46,48,49,50,52,53},
          {42,45,47,49,50,51,53,54},
          {43,46,48,49,51,52,54,55},
          {44,47,49,50,52,53,55,56},
          {45,48,50,52,55,56,57,58},
          {46,49,51,53,55,57,58,59},
          {47,50,52,55,56,58,60,62},
          {48,52,53,55,57,59,62,63},
          {49,53,54,56,58,60,63,64},
          {50,54,55,56,57,61,64,65},
          {51,55,56,57,58,62,65,66},
          {52,56,57,58,59,60,61,62},
          {53,57,58,59,60,61,62,63},
          {54,55,56,60,61,62,63,64},
          {55,56,57,61,62,63,64,65},
          {56,57,58,62,63,64,65,66} };

byte Array26[16][8]=                                     
        { {40,45,46,48,49,50,52,53},
          {42,45,47,49,50,51,53,54},
          {43,46,48,49,51,52,54,55},
          {44,47,49,50,52,53,55,56},
          {45,48,50,52,55,56,57,58},
          {46,49,51,53,55,57,58,59},
          {47,50,52,55,56,58,60,62},
          {48,52,53,55,57,59,62,63},
          {49,53,54,56,58,60,63,64},
          {50,54,55,56,57,61,64,65},
          {51,55,56,57,58,62,65,66},
          {52,56,57,58,59,60,61,62},
          {53,57,58,59,60,61,62,63},
          {54,55,56,60,61,62,63,64},
          {55,56,57,61,62,63,64,65},
          {56,57,58,62,63,64,65,66} };

byte Array27[16][8]=                                     
        { {40,45,46,48,49,50,52,53},
          {42,45,47,49,50,51,53,54},
          {43,46,48,49,51,52,54,55},
          {44,47,49,50,52,53,55,56},
          {45,48,50,52,55,56,57,58},
          {46,49,51,53,55,57,58,59},
          {47,50,52,55,56,58,60,62},
          {48,52,53,55,57,59,62,63},
          {49,53,54,56,58,60,63,64},
          {50,54,55,56,57,61,64,65},
          {51,55,56,57,58,62,65,66},
          {52,56,57,58,59,60,61,62},
          {53,57,58,59,60,61,62,63},
          {54,55,56,60,61,62,63,64},
          {55,56,57,61,62,63,64,65},
          {56,57,58,62,63,64,65,66} };

byte Array28[16][8]=                                     
        { {40,45,46,48,49,50,52,53},
          {42,45,47,49,50,51,53,54},
          {43,46,48,49,51,52,54,55},
          {44,47,49,50,52,53,55,56},
          {45,48,50,52,55,56,57,58},
          {46,49,51,53,55,57,58,59},
          {47,50,52,55,56,58,60,62},
          {48,52,53,55,57,59,62,63},
          {49,53,54,56,58,60,63,64},
          {50,54,55,56,57,61,64,65},
          {51,55,56,57,58,62,65,66},
          {52,56,57,58,59,60,61,62},
          {53,57,58,59,60,61,62,63},
          {54,55,56,60,61,62,63,64},
          {55,56,57,61,62,63,64,65},
          {56,57,58,62,63,64,65,66} };

byte Array29[16][8]=                                     
        { {40,45,46,48,49,50,52,53},
          {42,45,47,49,50,51,53,54},
          {43,46,48,49,51,52,54,55},
          {44,47,49,50,52,53,55,56},
          {45,48,50,52,55,56,57,58},
          {46,49,51,53,55,57,58,59},
          {47,50,52,55,56,58,60,62},
          {48,52,53,55,57,59,62,63},
          {49,53,54,56,58,60,63,64},
          {50,54,55,56,57,61,64,65},
          {51,55,56,57,58,62,65,66},
          {52,56,57,58,59,60,61,62},
          {53,57,58,59,60,61,62,63},
          {54,55,56,60,61,62,63,64},
          {55,56,57,61,62,63,64,65},
          {56,57,58,62,63,64,65,66} };

byte Array30[16][8]=                                     
        { {40,45,46,48,49,50,52,53},
          {42,45,47,49,50,51,53,54},
          {43,46,48,49,51,52,54,55},
          {44,47,49,50,52,53,55,56},
          {45,48,50,52,55,56,57,58},
          {46,49,51,53,55,57,58,59},
          {47,50,52,55,56,58,60,62},
          {48,52,53,55,57,59,62,63},
          {49,53,54,56,58,60,63,64},
          {50,54,55,56,57,61,64,65},
          {51,55,56,57,58,62,65,66},
          {52,56,57,58,59,60,61,62},
          {53,57,58,59,60,61,62,63},
          {54,55,56,60,61,62,63,64},
          {55,56,57,61,62,63,64,65},
          {56,57,58,62,63,64,65,66} };


void setup(){ 

Serial.begin(9600);

}


void loop(){

Serial.print(var1);

  }

Se Verificate o compilate questo codice, il conteggio Flash utilizzata vi dirà 2248 bytes se togliete le array darà lo stesso valore, se togliete "Serial.print(var1)" e "Serial.begin(9600)" il conteggio scende a 466 bytes con 110 variabili int dichiarate? la stessa quantità occupata da una sola variabile dichiarata??

Mi sembra strano e vorrei capirci qualcosa!!

Qualche idea??

Mi ripeto, le variabili dichiarate globali NON rientrano in quel conteggio della Flash ma in quello della SRAM. Per variare quel conteggio devi mettere più comandi nella setup() o nella loop()

Ogni libreria portà con sé delle funzioni, e le funzioni sono "codice" che occupa la Flash. La Serial è una libreria molto complessa ed occupa un sacco di memoria, togliendola dallo sketch la dimensione del programma cala di molto.

Per capire cos'è che occupa la RAM, leggiti questo articolo.

Ho capito che le variabili vengono salvate in SRAM, ma quello che non capisco è: Se il compilatore le "trasferisse in RAM" al momento della compilazione, essendo la RAM volatile, allora perchè poi al riavvio di Arduino funziona tutto correttamente? non avrebbe dovuto azzerare queste variabili?

Quindi secondo me, il codice compilato viene trasferito in Flash, la MCU all'accensione, carica i dati che vanno in SRAM e incomincia la sua funzione eseguendo ciò per cui è stato programmato, quindi non capisco ancora perchè il conteggio della flash utilizzata non aumenta. Se fosse allocata in RAM al momento della compilazione, mi spiegate come poi ci ritroviamo questi valori funzionanti?? e poi, come ricorderebbe i nomi delle variabili se allo spegnimento del MCU i dati in RAM si perdono? Quindi per logica deduzione, le variabili vanno comunque copiate dentro la flash al momento della compilazione, che poi esse vengano copiate/allocate in RAM questo è un'altra cosa o no?

Sono al corrente dello spazio occupato dalla funzione Serial.begin() e Serial.print() lo so che queste function nascondono una mole di codice C puro, così come tutte le librerie e funzioni facili/semplici da usare dell'IDE rispetto all' ANSI C, e su questo non ci piove, ci mancherebbe che non occupasse flash tutto quel codice. E lo spazio necessario alle variabili e array che non vedo perchè non viene occupato!

dal link indicatomi cito:

"Qualunque variabile che viene utilizzata in un programma deve essere, prima di venir utilizzata, copiata in RAM! Questo è per via dell’architettura Harvard, che separa la memoria Flash dove risiede il codice dalla memoria SRAM dove “vivono” i dati. "

Per me è ovvio che le variabili saranno usate in RAM e sul fatto che bisogna stare attenti a non usare tutta la RAM disponibile pena blocchi o risultati strani per via dell'invasione dello stack e/o del Heap nell'area dell'altro.

altra citazione:

"... Perciò quando la vostra stringa viene trasportata in RAM, anche qui occuperà lo stesso quantitativo di memoria che occupava in Flash: “CIAO” occuperà 4 byte, tanti quanti la stringa “AIO” nonostante questa sia lunga 3 byte."

Quindi ratifica quello che penso, le variabili (dichiarate e/o inizializzate) presenti nello sketch vengono copiate nella Fash e poi copiate nella RAM al riavvio della MCU.

Scusate, se sono un pò "tarato" é vero, sono un "principiante" ma con dati memorizzati in flash ci lavoro tutti i giorni (lavoro con le centraline delle macchine stradali e da competizione) e so la differenza tra questa e la RAM (quando faccio una variazione alla mappa di una ECU da Corsa i dati vengono inviati alla RAM, se mi dimentico di indicare alla ECU di salvarli in Flash i dati vanno persi allo spegnimento del quadro!

E mi ri-ripeto; QUEL conteggio ti dice l'occupazione del codice (del programma) in FLASH ad [u]esclusione[/u] delle variabili perchè le variabili NON occupano la Flash. Se invece installi l'IDE modificato di Eried, quello mi semba ti dice sia quanta Flash che quanta Ram occupi http://forum.arduino.cc/index.php/topic,118440.0.html anche se la ram occupata è solo teorico/iniziale perchè se fai allocazioni dinamiche, solo a runtime lo saprai.

Ok, ho capito il concetto, nel conteggio non si prende in considerazione le variabili/constanti/array che siano, ma sul fatto che non vadano a finire nella flash, scusa ma ho qualche dubbio. Se non vengono caricate dall'ide nella flash, come funziona lo schetch al riavvio se la RAM allo spegnimento perde il suo valore? (io mi riferisco al nome con cui chiamo le variabili, che poi il contenuto sia SEMPRE in RAM una volta in funzione lo sketch questo è assodato).

Scusami, non è per portare la contraria...

Tieni anche conto che le variabili dichiarate ma non usate il compilatore le scarta.

Prendi il seguente codice:

int a = 0;
void setup() {}
void loop() {
  a++;
}

Questo codice occupa 484 byte di Flash e 11 byte di RAM. Nel momento in cui lo salvi in Flash, il compilatore inserisce nel codice il valore "0" perché questo è quello di inizializzazione di "a". Al momento dell'esecuzione, il compilatore vede che c'è la definizione "int a = 0" e: 1) alloca in RAM 2 byte (tipo int) 2) assegna ad "a" il valore 0

Durante l'esecuzione del loop, incrementa "a".

Come vedi, finché non viene eseguito il codice, nella flash del chip c'è solo il valore iniziale. Se poi usiamo un array la cosa a livello di flash cresce di poco ma a livello di run, durante l'esecuzione cresce eccome.

int a[10];
void setup() {
}
void loop() {
  a[0]++;
}

Questo codice occupa sempre 484 byte ma ben 29 byte di Ram: viene usata solo 1 cella dell'array ma l'occupazione di RAM è maggiore perché abbiamo 18 byte in più (1 array con 10 celle int sono come 10 int, quindi 20 byte, ma 2 li avevamo anche nel precedente caso, per cui 18 byte in più).

Per la RAM ho sperimentato con freeRam() messo in diverse parti dello sketch e per ora sono a quota 1495 byte quindi un pò troppo alto considerando che sono ancora agli inizi della stesura e manca ancora tanto da scrivere, e per questo so di dover ottimizzare riducendo il numero di variabili global ed implementare di più quelle locali (in modo da "riciclare" in RAM con il Constructor/destructor).

Gia ho ridotto passando all'uso estensivo di tipo "byte" invece del solito "int" dovendo anche scendere a "compromessi" su alcuni dati che in realtà dovrebbero essere INT vuoi perchè superano i 255 di byte voui perchè contenenti dati negativi, quindi vi ho implementato dei fattori di moltiplicazione/divisori o anche sottrattori.

Grazie per questa notizia,

Tieni anche conto che le variabili dichiarate ma non usate il compilatore le scarta.

conferma le mie prove. Infatti ho fatto qualche esperimento estremo ed effettivamente avevo notato che in base alle chiamate alle variabili fatte da function o da loop(), la quantità di memoria FLASH cambiava ed anche radicalmente. Quindi se dichiaro variabili che in realtà poi non uso, non devo preoccuparmi, il compilatore le scarterà. Forse è questa la causa per la quale il conteggio rimane basso, ho tantissime variabili ed array dichiarate ed inizializzate ma per ora, poche decine sono richiamate nelle varie function che ho.

Leo72: Ok capisco perfettamente la tua spiegazione con esempi, lo so che le variabili vengono copiate e manipolate in RAM, ma se sono SOLO in RAM, perchè quando spengo e riaccendo Arduino o MCU qualsiasi, i dati che avevo inizializzati me li ritrovo? sapendo che il contenuto della RAM è volatile, non dovrebbe essere così, quindi io deduco che quel NOME di Variabile deve essere per forza anche memorizzato in FLASH insieme al programma, altrimenti, come fa a sapere che a=10? è questo quello che volevo dire prima, se per caso non mi sono fatto capire. Sulla RAM so che se dichiarassi un array[10][10] sto dicendo di riservare 100 byte di RAM ed è per questo che ho fatto i conti sulla somma di tutte le array che dovrò avere, infatti poi, i dati in esse andranno salvate in EEPROM e lì, il limite è 1024 bytes. Quindi sono consapevole di usare al massimo 1/2 capacità RAM del 328p, ai quali vanno aggiunti variabili, costanti, calcoli vari, contatori, puntatori ecc...

Grazie per la dritta e per la spiegazione con esempio.

Il motivo è quello che ti ha detto nid69ita, solo che non ha spiegato chi e perché. La fase finale del processo di build è il la fase di collegamento (linking) , che risolve i simboli, cioè i nomi delle variabili, funzioni ecc e li sostituisce con l'indirizzo "fisico" in ram o in flash per cui i simboli non esistono più dopo la compilazione, ma c'è un modo di per dire al linker di mantenere i simboli ovviamente il file eseguibile si gonfia per cui è utile mantenere i simboli solo nella fase di sviluppo o debug.

Al momento stai sperimentando e quindi ci sta che dichiari tutte quelle variabili globali, ma già adesso avresti dovuto notare come sia noioso tenere traccia di tutte quelle variabili e se nel codice ti serve una variabile index si fa presto, ma se le variabili index sono 10 sei costretto a chiamare tutte in modo diverso: idx, index, currentIdx, oldIndex ecc, in questo modo non vai lontano e ci vuole una scatola di medicine per vincere il mal di testa.

Per cui quando ti stanchi e noti dei limiti e non sai a cosa attribuirli, es il linguaggio, io non sono capace, il compilatore, ecc sappi che il problema è conosciuto ed aggirato progettando il software in modo modulare.

Piccoli pezzetti di codice contenuti in funzioni dove si creano le variabili locali che occupano spazio di RAM per tutta la durata della funzione. Pensa alla possibilità di scomporre il tuo progetto in oggetti, cioè avrai un display, dei led, una memoria eeprom su i2c ecc, che servono per il progetto embedded che ad esempio si chiama Strunz Controller che avrà esso degli stati, es lo Strunz Controller è in modo modifica parametri utente, se modifichi un parametro metti a 1 la variabile di StrunzController.changed = 1;, quando si esce dalla modalità modifica se StrunzController.changed == 1 salva i dati.

Vantaggi: sai sempre (quasi) dove si trovano la variabile, e se non lo sai vuol dire che la posizione non è stata scelta correttamente e quindi la sposti. Tutte le variabili che mantengono uno stato valido globalmente vanno messe in una classe o struct globale che a sua volta può contenere altre struct o o oggetti di classe. In breve devi scrivere una libreria di funzionalità concettualmente simile a quella di Arduino Core e se sei davvero bravo poi trovi il modo di riusare il codice in più punti come avviene in SerialHardware e tutte le altre classi come SoftwareSerial ecc dove viene riusato "print", sia riusare parti del Core o parti delle tue classi dentro altre tue classi.

Quindi le struct o class (che in C++ sono molto simile) sono dei contenitori di dati a cui associare delle funzioni.

Occhio a pensare prematuramente alle ottimizzazioni del codice, cioè prima impieghi del tempo per organizzare il codice e poi quando funziona o ai pezzi funzionanti dai una controllata per vedere se si può risparmiare spazio ram o flash, ma solo in previsione di progetti che sai per certo essere grandi e pieni di funzionalità anche accessorie, diversamente hai solo perso tempo perché la ram e flash ti sarebbe comunque bastata. Ciò non vuol dire che devi usare un 'int' se sai già a priori che non arriverà mai a superare il valore di 255, ma per esempio vuol dire usa il float (4 byte) per 50 variabili 200 byte se poi sei a corto di ram grazie alla programmazione ad oggetti e alla progettazione attenta ti ci vorrà mezza giornata per per trasformare i float in int16_t / 100 castati a float quando serve visualizzare il valore in virgola mobile. Oppure lasci i float e ti accorgi di potere risparmiare ram anche con una suddivisione del codice in più funzioni.

PS: non ho intenzione di confonderti le idee.

MauroTec:
Il motivo è quello che ti ha detto nid69ita, solo che non ha spiegato chi e perché. La fase finale del processo di build è il la fase di collegamento (linking) , che risolve i simboli, cioè i nomi delle variabili, funzioni ecc e li sostituisce con l’indirizzo “fisico” in ram o in flash per cui i simboli non esistono più dopo la compilazione, ma c’è un modo di per dire al linker di mantenere i simboli ovviamente il file eseguibile si gonfia per cui è utile mantenere i simboli solo nella fase di sviluppo o debug.

@Mauro, ma sugli Atmel l’indirizzo è fisico (quindi fisso) o è relativo, in base a dove il programma inizierà ad allocare ?
Dipende dal tipo di MCU (328, 2650, etc.) ?

Grazie Mauro, sei stato chiaro ed esaustivo come sempre (per quello che ci capisco per il momento, ma stiamo imparando...) Purtroppo, proprio per il fatto che sono alle prime armi nella programmazione in C/C++ è che ho usato molte variabili Global, sono consapevole della poca ergonomia nell'utilizzo di variabili locali, propio perchè ancora non capisco bene il passaggio di valori/variabili tra funzioni con dichiarazioni local, e tanto meno le struct che ho idea di cosa e per cosa servano ma non ne comprendo ancora del tutto come fare, quindi he preferito concentrarmi su cosa so fare limitando i "fuori campo" ma questo non toglie che man mano ne saprò di più, non vara a rivedere le cose che potrei fare megli. Per fortuna gia uso una struttura a blocchi, ma con la pecca che gran parte delle variabili/costant/array è global e quindi poco eficiente per la RAM e per quello che tu hai spiegato. Per quanto riguarda la spiegazione di cosa fa il compilatore ne avevo una idea e forse non mi sono saputo spiegare, lo so che quello che io chiamo " byte MiaVariabile=0;" il compilatore lo tradurra in valori/indirizzi byte direttamente quindi i "nomi" saranno d'utilità a me, non alla MCU che in realtà userà i registri ecc... Quello che dicevo prima è che indipendentemente da ciò, questi "riferimenti" saranno presenti anche dentro la flash altrimenti come fa a ripartire il programma una volta spento arduino? Quindi all'accensione, il Core della MCU andrà a leggere la flash, dove nella parte iniziale, andra ad allocare nei registri/indirizzi di RAM ciò che ha indicato in flash ovvero quello che resta delle nostre "variabili/costanti/array" ma sotto forma di codice macchina o quasi.

Per fortuna ho capito che se dichiaro variabili/const/array a vanvera e poi ne uso solo alcune, il compilatore spazzerà via tutto quello che non viene usato in setup() o loop() o altre function. Questo non lo sapevo e ho appena sperimentato ed arrivando alla stessa conclusione prima che mi sia stata suggerita! (ho creato un super sketch con un casino enorme di variabili, array a go go, e solo alcune chiamate Serial.print di dati a caso presi dentro le array, l'idea era capire se l'IDE mi avrebbe avvertito del superamento dello spazio flash (ci sono qualcosa come 80 array[16][8] e 30 array[56][56] !!!) lo ha compilato e programmato senza battere ciglio, ma era evidente che la RAM era corrotta, in sostanza, se richiedevo un Serial.print di un valore presente nelle array "piccole" tutto funzionava regolarmente, mentre se tentavo di chiedere un dato di un array di quelli mostruosi, o si bloccava o andava di matto, ovviamente, la ram è stata corrotta perchè troppo grossa un'array del genere supera di quasi il doppio della RAM disponibile!! e li, ho visto che a seconda di che array accedevo (grande o piccola o più di una grande ecc) che i byte Flash passavano da 2490 a 9500 bytes ! se il terget erano array "umane" i risultati erano intermedi e funzionavano, se prendevo un'array mostruosa occupava tanta flash e non funzionava più!

Come si dice, sbagliando si impara! per fortuna queste sono le cose che ci fanno imparare di più, imparare dai propri ed altrui errori è stato sempre il mio motto!

Vedrò di studiare come si fanno le struct, e imparare a passare i dati tra function.

Grazie a tutti per l'aiuto. Antonio

nid69ita:

MauroTec: Il motivo è quello che ti ha detto nid69ita, solo che non ha spiegato chi e perché. La fase finale del processo di build è il la fase di collegamento (linking) , che risolve i simboli, cioè i nomi delle variabili, funzioni ecc e li sostituisce con l'indirizzo "fisico" in ram o in flash per cui i simboli non esistono più dopo la compilazione, ma c'è un modo di per dire al linker di mantenere i simboli ovviamente il file eseguibile si gonfia per cui è utile mantenere i simboli solo nella fase di sviluppo o debug.

@Mauro, ma sugli Atmel l'indirizzo è fisico (quindi fisso) o è relativo, in base a dove il programma inizierà ad allocare ? Dipende dal tipo di MCU (328, 2650, etc.) ?

Si è sempre fisico ovviamente ed è anche relativo. Io ho dato una spiegazione generale della fase di collegamento, non riferita specificamente ad AVR, quindi "fisico" è l'indirizzo della cella di memoria che su un PC a causa della virtual memory non è mai fisico, cioè il dato potrebbe trovarsi su disco e allora il sistema porta prima il dato in ram (dovrà far spazio in ram) assegnata al processo e rimappa gli indirizzi, se c'è un processo che richiede tempo CPU e non c'è ram libera il sistema può sfruttare gli stessi indirizzi fisici di prima rimappando gli indirizzi. Questo è il motivo che mi ha portato a scrivere "fisico" tra apici perché è fisico soltanto se non c'è virtual memory.

@hiperformance71 In C++ struct e class sono molto simili, pertanto prima studia struct in C, poi struct in C++ e una sbirciatina a class che è molto più complessa.

struct AState {  // il tipo è AState, cioè uno stato
    bool isReady;
    bool isSaved;
    // ecc ....
};

struct AState gState; // creo una variabile di tipo AState con nome gState che sta per global state (nome a piacere)

// Ora posso usare gState per salvarci un valore in isReady e in isSaved
gState.isReady = true; // vero cioè 1 o diverso da 0
gState.isSaved = false;

// in lettura
if (gState.isReady == true)
     ; // fai qualcosa visto che è pronto
else
    ; // non è pronto es. attendi che sia pronto  

if (gState.isSaved == false) {
    // ci sono dati non savati 
    // chiama funzione per salvare i dati
   // Ora i dati sono salvati e quindi 
   gState.isSaved = true;
}

// in qualche modulo che modifica i dati 
if (oldValue != currentValue) 
    gState.isSaved = false;
// cioè hai preso nota del fatto che i dati sono stati modificati e che devono essere salvati e ciò accadrà
// quando nel loop il codice precedente a questo viene eseguito.

isReady è una variabile membro (campo) di struct AState, AState può anche avere funzioni membro a cui si può accedere tramite l'operatore '.' punto. Una classe è simile ma puoi decidere nel dettaglio quali membri sono publici e quali privati o protetti. L'utilità dei dati protetti la capirai molto più in la quando vuoi creare una classe che eredita una o più classi.

Il vantaggio di struct o class e che se hai due parti di programma che ad esempio salvano una in eeprom e una in sdcard puoi continuare ad usare gState come modello valido creando due variabili di tipo gStateEEprom e gStateSd e riusare il codice che hai scritto, in più non sei costretto ad inventarti nomi di variabili simili che in fin dei conti servono a tenere traccia dello stesso stato. Questo è solo un esempio e struct può essere usato con profitto globalmente o localmente. Per le funzioni membro di classe ti servirà studiare bene il passaggio di parametri per valore e per riferimento, poi più in là ci metti anche i puntatori che però devono essere studiati separatamente anche prima di class e struct. Alla fine ti ritroverai a saper fare funzioni che prendono parametri e restituiscono qualcosa al chiamante, struct e class ecc ma non sarai capace di dividere il programma che stai scrivendo in class o struct e funzioni e questo sarà sempre così, perché sarà un problema da affrontare sempre.

Ciao.

@hiperformance: te lo avevo scritto che i dati di inizializzazione il compilatore li salva in flash. Altrimenti, come giustamente osservi anche tu, come potrebbe sapere il valore iniziale di una variabile? ;)

int a = 10 viene tradotto in una serie di istruzioni in assembly dove, oltre all'allocazione in RAM, c'è la scrittura del valore iniziale "10". Ma questa informazione è salvata in Flash, appunto, altrimenti non potrebbe sopravvivere al distacco dell'alimentazione.

MauroTec: Si è sempre fisico ovviamente ed è anche relativo. Io ho dato una spiegazione generale della fase di collegamento, non riferita specificamente ad AVR, quindi "fisico" è l'indirizzo della cella di memoria che su un PC a causa della virtual memory non è mai fisico, cioè il dato potrebbe trovarsi su disco e allora il sistema porta prima il dato in ram (dovrà far spazio in ram) assegnata al processo e rimappa gli indirizzi, se c'è un processo che richiede tempo CPU e non c'è ram libera il sistema può sfruttare gli stessi indirizzi fisici di prima rimappando gli indirizzi. Questo è il motivo che mi ha portato a scrivere "fisico" tra apici perché è fisico soltanto se non c'è virtual memory.

Si, ok @Mauro. Forse mi sono espresso male. Io chiedevo se su un AtMel328 l'indirizzo è fisso. Cioè c'e' un numero sempre uguale da dove iniziare. Se cosi fosse, anche sulle altre Atmel c'e' un indirizzo fisso e quale ? Ma dubito di questa cosa. Anche su questi micro l'indirizzo iniziale SRAM può variare, o no ? A volte ho leggiucchiato forum o forse anche datasheet e ho visto dei numeri di memoria fissi ma non sono sicuro. Abituato a PC (masm 386) dove indirizzi sono relativi, non capisco se queste MCU lavorano ad indirizzi "fissi".

Sulle MCU su cui non è stato attivato il bootloader, il PC (program counter) viene precaricato con $0000, quindi la CPU inizia ad eseguire il codice da lì. Dove c'è la jump table dei salti per ogni vettore. Il vettore di reset è ovviamente il primo, c'è il primo jump che manda a dove materialmente inizia il programma. Se invece hai riservato un'area per il bootloader, il PC viene caricato con l'indirizzo del 1° byte dell'area riservata al bootloader (che varia in base alla dimensione del bootloader stesso).

Grazie @Leo.