Ridimensionare array

Però mi interesserebbe approfondire il discorso malloc() e realloc()

Se io ho nello sketch
PROGMEM prog_uint16_t charSet[] = { 65, 32, 16, 10, 11};
si potrebbe modificare il contenuto di quell' array durante l'esecuzione del programma o almeno una sola volta nel setup per esempio?
Se io potessi prendere 20kb della flash e scriverla attraverso la lettura di un file su sd oltre ai 4 della eeprom sarebbe la soluzione a un sacco di problemi varrebbe per me la pena di starci su anche 2-3 mesi :cold_sweat:
Sarebbe la stessa cosa che riscrivere lo sketch da remoto?

ciao

prova ma dubito, per il fatto che è PROGMEM.

per la eeprom il discorse è differente, salvi nella cella 0 e 1 il numero di elementi e poi crei un array di quelle dimensioni del tipo di dato che ti interessa e poi fai un puntatore a byte che punta all'indirizzo dell'array dichiarato, e ogni byte letto dalla eeeprom finisce nel puntatore byte, il quale poi incrementerà il suo indirizzo di 1, il tutto finchè indirizzo di partenza < indirizzo di partenza + sizeof(tipo di dato salvato)*numero elementi

Per la eeprom già la uso e sono a tappo :), quali argomenti dovrei studiare per manipolare la flash? ovviamente parlo di arduino

grazie ciao

pablos:
Se io ho nello sketch
PROGMEM prog_uint16_t charSet[] = { 65, 32, 16, 10, 11};
si potrebbe modificare il contenuto di quell' array durante l'esecuzione del programma o almeno una sola volta nel setup per esempio?

Certo che puoi farlo, i 328 permettono di scrivere la flash da programma previa allocazione dello spazio da utilizzare.
Leggi qui e avrai la soluzione alla tua domanda.

astrobeed:
previa allocazione dello spazio da utilizzare.

quindi lo spazio allocato non è dinamico ma fissato a tempo compilazione?

lesto:
quindi lo spazio allocato non è dinamico ma fissato a tempo compilazione?

La flash per forza di cose visto che normalmente ospita solo il programma, se non riservi prima lo spazio per le eventuali variabili poi c'è il reale rischio di andare a scrivere sul programma stesso.

infatti quello che immaginavo, magari esiste qualcosa per risalire alla flash utilizzabile ed auto allocarla come array di byte, così con un pò di magia dei puntatori si può usare tutta.. ma è roba assai complessa.

Certo immaginavo che era come giocare al piccolo chirurgo, devo stare attento a rimenere nei limiti della Flash usata e fuori dalla Sram delle variabili, anche se la sram dovrebbe essere al di fuori dei 256k in un altra casa :slight_smile:

Flash Memory 256 KB of which 8 KB used by bootloader
SRAM 8 KB
chissà quegli 8 kb dei bootlooader.... saranno all'inizio o in fondo? :slight_smile:

c'è il reale rischio di andare a scrivere sul programma stesso.

Bhe si finchè scrivo sul programma, si schianta, ma resettando risolvo, ma se scrivo nel bootloader sono guai seri.

Però di solito la cella di memoria vuota restituisce in decimale il byte 255, in qualche modo dovrei riuscire a vedere quali celle sono impegnate.

Bho... io ci voglio provare è una cosa più grande di me :smiley: :smiley:

Grazie astro interessante quel link, forse fa al caso mio

Però di solito la cella di memoria vuota restituisce in decimale il byte 255, in qualche modo dovrei riuscire a vedere quali celle sono impegnate.

da quando questo? a me non risulta, anzi ci sarà dentro l'ultimo valore memorizzato... esattamente come in una variabile non inizializzata!

lòa parte boot-loader CREDO sia protetta, ma magari mi sbaglio. mi pare che il bootloader sia più in fondo, poi schiacciato sul fondo il programma, poi schiacciati all'inizio i vettori di interrupt... ma non sono assolutamente sicuro, anzi! forse astro lo sà, altrimenti guardando vecchie discussioni si è parlato relativamente spesso di queste cose

da quando questo? a me non risulta, anzi ci sarà dentro l'ultimo valore memorizzato

Sì certo, anche tu hai ragione, ma non avendo scritto programmi più lunghi di 58k dovrò trovare delle celle vergini, almeno che per un pezzo siano tutte uguali :slight_smile: quello dovrebbe farti capire che da un certo punto in poi non c'è mai stato nessuno :slight_smile:

la program memory parte da 0x0000

@pablos:
il bootloader occupa sempre la parte più alta della memoria Flash.

@lesto:
una cella FLASH o EEPROM erasata contiene $FF come valore "vuoto".

Ho letto un testo di Banzi dove uno schema a blocchi mette memory program 0x0000 e il boot lo mette in fondo comunque fa lo stesso a me serve il centro ahahahahah.

@Astro il link che hai messo avr-libc: Data in Program Space mi scrive dei byte prestabiliti usando uno spazio della flash,
vedo solo dei pgm_read, ma non dei pgm_write
io chiedo di poter scrivere su una cella della flash come faccio con la eeprom a prescindere che rischio di andare sopra allo sketck , che istruzioni devo usare per leggere e scrivere su una posizione?

grazie ciao

Se tu avessi un vettore v1 di dimensione x e vorresti avere un vettore v2 di dimensione y allora devi chiamare la funzione che ho scritto come nel seguente modo:

int * v2 = changeDim(v1, y);

A questo punto v2 contiene l'indirizzo a partire dal quale è memorizzato l'array in memoria.

int* changeDim(int* vector, int newDim){
    if (vector != 0 ){
        int* newArray = new int[newDim];
        int vectorLen = newDim;
        for (int var = 0; var < vectorLen; ++var)
            newArray[var] = vector[var];

        return newArray;
    }
    else
        return (int*)0;
}

La mia funzione restituisce vettore di iteri ma basta cambiare il tipo per ottenere un vettore di diverso tipo.

ci sono 3 errori:

  1. stai creando garbage non distruggendo il vecchio vettore (il c++ non ha un garbage collector come java o simili, la spazzatura la si porta fuori a manina)
  2. alla funziona non puoi passare un array, poichè un array ha indirizzo costante, e quindi non modificabile tramite malloc o new
  3. dai per scontato che la "new" abbia successo, cosa non vera. se non c'è ram disponibile la new/malloc ritorna NULL

ci sono anche 3 imprecisazioni secondo me:

  1. stai confondendo new (usato per le classi) con malloc e compagnia bella (la new in realtà poi chiama la malloc, ma il punto è che confondi C e C++ inutilmente)
  2. prima di fare una malloc prova a fare una realloc: questa non solo POTREBBE (non ne sono sicuro) funzionare anche con gli array, visto che non modifica la posizione dell'array ma cerca solo di espanderlo, ma appunto per questo comportamento non è necessario copiare il contenuto del vecchio vettore in quello nuovo risparmiando un sacco di cicli macchina, senza contre che nella fase di copia evita di occupare quasi il doppio di RAM necessaria dal tuo metodo (vecchio vettore + nuovo vettore).
    lo svataggio della realloc() è che spesso fallisce perchè subito dopo l'array la RAM è già usata da altre variabili... in tal caso si fa il fall-back a malloc
return (int*)0;

è illeggibile, non sono neanche sicuro di interpretarla bene. forse intendevi

return null;

lesto, non so se te ne sei accorto ma nelle ultime versioni dell'IDE (non so se è già presente nella 1.0.3 perché io ho una 1.0.4 beta, compilando spesso dai sorgenti) sono stati inseriti new e malloc anche nell'Arduino.
Se guardi in core/Arduino trovi i file malloc.c, new.cpp e new.h
In new.h si legge poi:

Header to define new/delete operators as they aren't provided by avr-gcc by default

Non sapevo che avr-gcc non gestisse new e malloc... mi suona strano

leo non metto in dubbio questo, le malloc sono sempre esistite, altrimenti non avresti tutto ciò che usa RAM in modo dinamico (anzi, mi sa tutto ciò che usa ram).
La NEW non esisteva ed è stata aggiunta, ma comunque poteva essere creata ad-hoc, visto che come dicevo alla fine non è altro che una malloc con un poco di logica in più, e la delete è una free.

Quello che sto cercando di far capire è che quelle funzioni sono usate in modo magari non errato, ma sicuramente molto border-line, sicuramente non da dare a cuor leggero a chi non capisce cosa sia un puntatore (reply #2)

osservando meglio l'empio di uso postato, alessandro87 ha correttamente usato un puntatore per ricevere il risultato, e non ha sovrascritto perdendo per sempre l'indirizzo di v1, creando garbage.
corretto:
int * v2 = changeDim(v1, y);
quello che mi aspeto facia un principiante:

v1 = changeDim(v1, y); //errore, la vecchia zona di memoria di v1 è diventata garbage ovvero non più utilizzabile perchè contrassegnata come allocata, ma non più utilizzabile/deallocabile perchè non sappiamo più dov'è

rimane comunque sicuramente l'errore del punto 3. e anzi, aggiungerei un punto 4. nelle "imprecisazioni secondo me": mi sono lamentato della return ma non della

if (vector != 0)

che è più leggibile come

if (vector != null)

quindi:

int* changeDim(int* vector, int newDim){
    if (vector != null ){
        int* newArray = realloc( newDim*sizeof(int) );
        if (newArray != null){
            vector = newArray ;
            return vector;
        }
        newArray = malloc( newDim*sizeof(int) );
        if (newArray != null){
            for (int var = 0; var < newDim; ++var)
                newArray[var] = vector[var];
            free(vector);//dealloca la ram
            vector = newArray;
            return vector;
         }
    }
    return null;
}

Ciao lesto,

Per quanto riguarda gli "errori":

  1. Il vecchio vettore non si può distruggere perché è creato staticamente infatti nel primo post carletto scriveva che aveva un vettore fatto cosi: byte mioarray[35]. E comunque alla mia funzione non interessa quello che accade sopra e anche se gli interessasse non è di certo sua la responsabilità di eliminare oggetti posti fuori dalla sua scope .
  2. Alla funzione passo l'indirizzo a partire dal quale è memorizzato l'array in memoria, non voglio modificare nulla voglio soltanto leggere i valori presenti.
  3. Qua ti do ragione.. ma un gioco con arduino non è safety critical. In altre circostanze c'è da tenere conto di questo aspetto.

Per quanto riguarda le imprecisioni

  1. Arduino permette l'utilizzo del c++ e io programmo in c++ perchè mi piace di piu e perchè è a oggetti. (conosco gente che programma a oggetti anche con il c ma questo non fa per me)
  2. Ho deciso di copiare i vecchi numeri per gioco.
  3. (int*) 0 è un exp e si legge cosi: 0 è una var e faccio il cast a puntatore intero. Equivalente a fare questo int* var = 0; return var;(leggi la grammatica del c++) inoltre in c++ la definizione di NULL è 0. Stroustrup: C++ Style and Technique FAQ .

E ultima cosa ecco i tuoi errori:

Prova.cpp: In function ‘int* changeDim(int*, int)’:
Prova.cpp:31:19: error: ‘null’ was not declared in this scope
Prova.cpp:32:53: error: invalid conversion from ‘unsigned int’ to ‘void*’ [-fpermissive]
Prova.cpp:32:53: error: too few arguments to function ‘void* realloc(void*, size_t)’
In file included from /usr/share/arduino/hardware/arduino/cores/arduino/Arduino.h:4:0,
from Blink.cpp:10:
/usr/lib/gcc/avr/4.7.2/../../../avr/include/stdlib.h:338:14: note: declared here
Prova.cpp:37:47: error: invalid conversion from ‘void*’ to ‘int*’ [-fpermissive]
Prova.cpp:46:12: error: ‘null’ was not declared in this scope

Come puoi notare nell'ultima riga: ‘null’ was not declared in this scope.

Grazie e ciao :stuck_out_tongue:

ok i punti 1 e 2 però

ma un gioco con arduino non è safety critical. In altre circostanze c'è da tenere conto di questo aspetto.

insomma. la ram esaurita è una delle cose più difficili da debuggare su un'arduino, e cose del geenre non aiutano.

(conosco gente che programma a oggetti anche con il c ma questo non fa per me)

vorrei conoscere questa gente... il C non è ad oggetti, si può simulare il comportamento degli oggetti... e in pratica riscrivi il C++ :grin:

E ultima cosa ecco i tuoi errori:

hai ragione, scivo il codice a lavoro e ogni tanto dimentico di avvisare che non è nemmeno testato in compilazione.
Comunque NULL invece che null, la realloc vuole anche il puntatore all'array da incrementare, e tulle le alloc ritorano un puntatore a void (generico) che va castato in quello che ti interessa

codice compilabile:

int* changeDim(int* vector, int newDim){
    if (vector != NULL ){
        int* newArray = (int*)realloc(vector, newDim*sizeof(int) );
        if (newArray != NULL){
            vector = newArray ;
            return vector;
        }
        newArray = (int*)malloc( newDim*sizeof(int) );
        if (newArray != NULL){
            for (int var = 0; var < newDim; ++var)
                newArray[var] = vector[var];
            free(vector);//dealloca la ram
            vector = newArray;
            return vector;
         }
    }
    return NULL;
}

volendo si possono usare i puntatori void per rendere la funzione più generica (notare che non si possono usare gli indici o giocare con gli indirizzi nei puntatori void e quindi li casto ad array di byte, in alcuni compilatori non è necessario):

void* changeDim(void* vector, int newDim, int sizeOfType){
    if (vector != NULL ){
        void* newArray = realloc(vector, newDim*sizeOfType );
        if (newArray != NULL){
            vector = newArray;
            return vector;
        }
        newArray = malloc( newDim*sizeOfType );
        if (newArray != NULL){
            for (int var = 0; var < newDim*sizeOfType; var++)
                ((byte*)newArray)[var] = ((byte*)vector)[var];
            free(vector);//dealloca la ram
            vector = newArray;
            return vector;
         }
    }
    return NULL;
}

esempio di uso

int* array = (int*)malloc(sizeof(int)*16); //da 16
array = (int*)changeDim(array, 20, sizeof(int)); //a 20

edit:in fine ricorderei che non ci troviamo davanti a vero C o vero C++, tantè che fino a 5 o 6 mesi fa non esisteva la new o la delete.

bene.. sarà contento adesso carletto di avere queste soluzioni! Ciaoo e buonanotte