Problemi sizeof(Array[]) [Risolto]

Salve a tutti,
ho dei problemi appunto con l'utilizzo di sizeof(). Stavo usando la funzione per sapere il numero di elementi di un array di stringhe.
Per qualche motivo che non conosco mi bastava fare sizeof(Array[]) e dividerlo per 6 per ottenere esattamente il numero di elementi di questo array di stringhe.
Ora stavo cercando di implementare il lavoro che avevo già fatto in un'unica funzione, ma non capisco come mai quando utilizzo sizeof() esso mi dia come risultato sempre 2, anche cambiando il numero di elementi della stringa.
Vi posto il codice:

String CMD[]={"giallo","rosso","verde","13"};
int Rele[]={3,4,5,13};
int eleme = sizeof(CMD)/6; //numero di elementi nell'array. ATTENZIONE conta anche elementi vuoti
boolean State[4]; 


void setup() {
  Serial.begin(9600);
  for(int i=0; i<=eleme-1; i++){
    pinMode(Rele[i], OUTPUT);     
  }//configuro tutti i pin di Rele come output
}

void loop() {  
  
  while(!Serial){}
  while(Serial.available()==0){}//attesa comando
  
  String cmd = Serial.readStringUntil('\n'); //legge stringa fino a quando vado a capo
  cmd.replace("/n","");
  cmd.replace("/r","");
  
  arrayCommand(CMD,Rele,State,cmd);
  
  }

void arrayCommand(String CMD[],int outputPin[], boolean State[], String command){

  int elem = sizeof(CMD);
  
  Serial.println(sizeof(CMD)); //debug, QUA IL PROBLEMA   
  
  //Serial.println(command); //debug
 
  int a= -1; //variabile per memorizzare posizione del comando
  
  for(int i=0; i<=elem-1; i++){
    if(command == CMD[i]){
      a = i;
    }
  }//ATTENZIONE: nel caso siano presenti due elementi uguali verrà restituito il valore dell'ultimo
  
  //Serial.println("a: "+ (String)a); //debug

  if(a==-1){
    Serial.println("Comando non riconosciuto");      
  }//nel caso il comando non sia contenuto nell'array

  else{
    State[a]= !State[a];
    digitalWrite(outputPin[a], State[a]);
    //Serial.println("Rele numero: "+(String)Rele[a]);//debug
    //Serial.println("Stato: "+(String)State[a]);//debug
  }//cambia stato al pin corrispondente al comando e accende/spegne il pin corrispondente
  
}

La funzione da me creata è appunto arrayCommand, se riusciste a darmi un aiuto ve ne sarei grato :cold_sweat: :cold_sweat:

Quella cosa ti funzionava praticamente per caso. In C gli array "nativi" sono statici, non possono cambiare di dimensione, per cui tanto vale specificarla quando li crei. Solitamente si fa qualcosa tipo:

#define N_ELEM 4

String CMD[N_ELEM]={"giallo","rosso","verde","13"};
int Rele[N_ELEM]={3,4,5,13};

Il che, tra l'altro, permette al compilatore di segnalarti un eventuale numero errato di elementi usati per l'inizializzazione.

Se gli array devono poter contenere un numero variabile di elementi, allora devi definire una dimensione massima e tenere una variabile con il numero effettivo di elementi, che dovrai passare alle funzioni insieme al vettore. In C non c'è modo di fare diversamente. In C++ potresti eventualmente ricorrere ad alcune classi della STL, ma su Arduino (sebbene ve ne sia un porting), eviterei.

PS: Non sono sicuro che un array di String definito in quel modo funzioni. Meglio usare const char *.

sizeof è una funzione che viene eseguita al tempo di compilazione. Non può essere usata in runtime.
Il compilatore la interpreta come meglio può, nel primo caso tu gli dici quanta memoria utilizza l'array di String, e otterrai ovviamente sizeof(String)*n. sizeof(String) non è correlato alla dimensione della stringa in quanto questa viene sempre allocata nell'heap(memoria dinamica) per ovvie ragioni, per tanto sizeof(String) è una costante. Nella funzione tu stai richiedendo al compilatore di dirti quanto spazio occupa nella memoria locale il puntatore CMD ad un array di String, Ricordiamo che le variabili locali quali argomenti finiscono nei registri general purpose del AVR, i registri possono essere usati accoppiati per ottenere variabili da 16bit come riportato al capito 7.4 del datasheet del ATmega328P. La memoria degli AVR ha un indicizzazione a 16bit, per tanto un puntatore richiede 2 parole di memoria a 8bit, quindi il risultato che ottieni è tutt'altro che strano. sizeof di un puntatore restituirà sempre 2 byte.

Ringrazio entrambi per le risposte, molto interessante :smiley: Ora almeno ho capito il perchè dei risultati!! :wink:
A questo punto la domanda è un'altra: essendo che la mia funzione deve funzionare per qualsiasi array, ho qualche alternativa? Oppure devo aggiungere un parametro da immettere nella funzione che corrisponda proprio al numero di elementi?
Intendo una cosa del tipo:

void arrayCommand(String CMD[], int numeroElementi, int outputPin [], boolean State[], String command){}

Mi pare sia esattamente quel che ho detto sopra...

Se gli array devono poter contenere un numero variabile di elementi, allora devi definire una dimensione massima e tenere una variabile con il numero effettivo di elementi, che dovrai passare alle funzioni insieme al vettore.

SukkoPera:
Mi pare sia esattamente quel che ho detto sopra...

Si si l'avevo capito scusa :grinning:
volevo sapere se c'era un'alternativa o è l'unica speranza

Mi pare di aver detto anche questo, immediatamente dopo:

In C non c'è modo di fare diversamente. In C++ potresti eventualmente ricorrere ad alcune classi della STL, ma su Arduino (sebbene ve ne sia un porting), eviterei.

SukkoPera:
Mi pare di aver detto anche questo, immediatamente dopo:

Perdonami, non parlo più! Grazie mille

RobertoBochet:
sizeof è una funzione che viene eseguita al tempo di compilazione.

Tutto corretto se parliamo di un vero compilatore C++, tutto da rivedere se parliamo di Arduino e del "casino" che hanno fatto con l'oggetto String(). :slight_smile:
Se provi lo sketch che allego scopri che sizeof(), usata come nel problema proposto, conta sei byte per ogni singolo array di char inserito, indipendentemente dalla reale lunghezza della stringa.
Presumibilmente vengono allocati 6 byte nella ram (segmento .bss) per ogni stringa perché rappresentano valori a 16 bit per l'address del puntatore, la dimensione dell'elemento e l'address fisico della prima locazione ram in cui è posto l'elemento, tocca verificare il sorgente della String per capire realmente come lavora.

tring CMD[] = {"1", "2", "3", "4"};
int Rele[] = {3, 4, 5, 13};
int eleme = sizeof(CMD);

void setup() {
  Serial.begin(115200);
  Serial.println(eleme);
  Serial.println(CMD[0]);
  Serial.println(CMD[1]);
  Serial.println(CMD[2]);
  Serial.println(CMD[3]);
}

void loop()
{

}

Ragiona, il C è un linguaggio sviluppato proprio per ragionare sull'informazione in termine matematico, se ho 8bit di informazione il compilatore assegnera solo 8bit per quella variabile, non ha senso affidargli 16 o 32 bit. Se creo un array questo porterà al suo interno la sola minima informazione cioè il numero di elmenti per l'informazione del singolo. Non avrebbe senso allocare piu byte di quelli che trasportano l'informazione minima, per tanto è importante (se necessario) conservare il numero di elementi nel array. Per capire questi argomenti sarebbe bene capire come funziona il compilatore e l'assembly.

Pariamo dal presupposto che "sizeof" NON è UNA FUNZIONE! "sizeof" è un OPERATORE, alla pari degli operatori +,-,etc..

Detto ciò la sizeof sull'oggetto stringa ritornerà quanto esso occuma in memoria, per capire meglio, le classi sono come delle strutture e se noi dichiariamo:

struct prova
{
    byte a;
    byte b;
};

int r = sizeof(struct prova);

r prenderà il valore di 2 byte.
Sapendo ciò se noi allochiamo spazio per un vettore e gli togliamo la sua dimensione otteniamo i numeri degli elementi, pertanto il codice corretto è il seguente:

String CMD[] = {"1", "22", "333", "4444","55555"};

void setup() {
  Serial.begin(9600);
  int eleme = sizeof CMD;
  Serial.print("dimensione totale:");
  Serial.println(eleme);
  Serial.print("Numero elementi vettore:");
  Serial.println(eleme / sizeof(String));
}

void loop()
{

}

che ritorna il seguente output:

dimensione totale:30
Numero elementi vettore:5

RobertoBochet:
Ragiona, il C è un linguaggio sviluppato proprio per ragionare sull'informazione in termine matematico, se ho 8bit di informazione il compilatore assegnera solo 8bit per quella variabile,

Un conto sono le variabili, dove la memoria allocata è quella definita dal suo tipo, un conto sono gli address e sul 328 sono a 16 bit, se viene costruito un puntatore questo è per forza di cose grande due byte, non a caso la flash degli Atmega è organizzata in word di due byte e i registri possono essere usati a coppie per contenere valori a 16 bit.

astrobeed:
Un conto sono le variabili, dove la memoria allocata è quella definita dal suo tipo, un conto sono gli address e sul 328 sono a 16 bit, se viene costruito un puntatore questo è per forza di cose grande due byte, non a caso la flash degli Atmega è organizzata in word di due byte e i registri possono essere usati a coppie per contenere valori a 16 bit.

Il post non era per te astro XD Era riferito a

Brunez3BD:
Si si l'avevo capito scusa :grinning:
volevo sapere se c'era un'alternativa o è l'unica speranza

vbextreme:
se noi allochiamo spazio per un vettore e gli togliamo la sua dimensione otteniamo i numeri degli elementi, pertanto il codice corretto è il seguente:

String CMD[] = {"1", "22", "333", "4444","55555"};

void setup() {
  Serial.begin(9600);
  int eleme = sizeof CMD;
  Serial.print("dimensione totale:");
  Serial.println(eleme);
  Serial.print("Numero elementi vettore:");
  Serial.println(eleme / sizeof(String));
}

void loop()
{

}



che ritorna il seguente output:


dimensione totale:30
Numero elementi vettore:5

Questo è corretto, ma funziona solo finché lo usi con array globali o locali. Se passi un array a una funzione tramite puntatore, la dimensione "si perde" e il risultato sarà sbagliato!

Comunque credo che stiamo solo confondendo le idee al povero OP :D.

SukkoPera:
Comunque credo che stiamo solo confondendo le idee al povero OP :D.

Che è la cosa che solitamente ci riesce meglio.
Il concetto finale è che è meglio evitare di usare String e usare array di char visto che alla fine ci troviamo a lavorare su una piccola mcu con pochissima ram e il C++ è il male assoluto in questi casi :smiley:

SukkoPera:
Questo è corretto, ma funziona solo finché lo usi con array globali o locali. Se passi un array a una funzione tramite puntatore, la dimensione "si perde" e il risultato sarà sbagliato!

Comunque credo che stiamo solo confondendo le idee al povero OP :D.

No beh, fino a questo punto stavo seguendo XD infatti stavo per rispondere come hai risposto tu (non con gli stessi termini ovviamente :smiley: ).
In definitiva mi conviene operare con array diversi o passare alla funzione anche il numero di elementi dell'array.
Quindi il "metodo" descritto sopra lo potrei usare al di fuori della funzione per ottenere il numero di elementi

Si, ma sizeof più che con gli array ha senso con le struct.
E come standard passa un array ad una funzione insieme al numero di elementi se questo è variabile.

Io direi che possiamo anche concludere qui :smiley:
il problema è risolto e ringrazio tutti per l'aiuto datomi :slight_smile:
Grazie mille e Buon anno a tutti (anche se in anticipo) :smiley: