Risolto:switch VS if

Ho una domanda da porre, e spero che chi si intende di programmazione e gli 'esteti' possa delucidarmi.
E' meglio uno switch o più if??
Ho scritto questo

 if (eventi[i] == 1) {
    bitSet(buffer_uscite, i - 6);
  }
  if (eventi[i] == 2 || eventi[i] == 0) {
    bitClear(buffer_uscite, i - 6);
    eventi[i] = 0;
  }

E poi con la funzione switch ho scritto questo

switch (  switch (eventi[i]) {
    case 0:
      bitClear(buffer_uscite, i - 6);
      break;
    case 1:
      bitSet(buffer_uscite, i - 6);
      break;
    case 2:
      bitClear(buffer_uscite, i - 6);
      eventi[i] = 0;
      break;
  }

Fanno entrambe la stessa cosa, ma quale mi consigliate di usare come forma?
Quale delle due espressioni è più veloce?

Rileggi e vedrai che non fanno la stessa cosa, in un caso la condizione eventi[i]==2 || eventi[i]==0 fa una cosa, nell'altro ne fa due diverse.

PS: l'indentazione questa sconosciuta...

Ho identato meglio...
Mah, non ho a disposizione il materiale per provare (con questa storia del covid le consegne si sono allungate), ma sulla carta mi sembrano se non uguali....simili...cioè il risultato finale mi sembra lo stesso...
O sbaglio??

Sbagli, due programmi simili non fanno la solita cosa

 a = a + 1;
 a = a -1 ;

sono molto simili ma fanno due cose diamtralmente differenti.
Così i due pezzi di codice (che penso siano stati trovati su internet piuttosto che scritti da zero da te) fanno cose differenti poiché il secondo if ha due condizioni in or, cosa che lo switch non ha ovviamente

Quello che hai scritto non è uguale perchè nel primo case (quello a 0) NON hai messo eventi[ i ] = 0;

Ora, il if con l'or lo puoi fare con switch, basta che usi il break; in maniera "creativa", ovvero sfrutti il fatto che il case è punto di ingresso ma solo il break fa "uscire" dal case, se non c'e' prosegue:

switch (  eventi[i])
{ case 1:
     bitClear(buffer_uscite, i - 6);         // entro con 1 ed esco
     break;
   case 0:                        // entro con 0 ma proseguo nel 2
   case 2:                        // entro con 2
     bitClear(buffer_uscite, i - 6);
     eventi[i] = 0;
     break;
 }

Per la tua domanda... quale più veloce... nessuna delle due, tanto il codice C viene convertito in assembly, con una serie di jump condizionati, ottimizzati dal compilatore ; probabilmente il risultato tra i due in linguaggio macchina è molto simile

manolomao:
Fanno entrambe la stessa cosa, ma quale mi consigliate di usare come forma?

Farebbero la stessa cosa se non avessi dimenticato di scrivere eventi[i] = 0; anche nel case 0.

Ma non sarebbero lo stesso la stessa cosa perché i case di uno switch sono (normalmente) mutuamente esclusivi, la forma con l'if avrebbe dovuto proseguire con else if, non con un altro if indipendente dal primo.

Oltre a questo i vari case dello switch testano solo l'uguaglianza con un valore intero, non condizioni più complesse che si possono testare solo con if.

Su quale usare, secondo me quella che di volta in volta nel caso specifico è più chiara da leggere/modificare.

Concord con nid69ita, stavo per scrivere esattamente la stessa cosa.

Claudio_FF:
Oltre a questo i vari case dello switch testano solo l'uguaglianza con un valore intero, non condizioni più complesse che si possono testare solo con if.

Questo molto importante, gli if possono avere dei test molto complessi, lo switch solo uguaglianza su numeri interi

In pratica se devi confrontare solo dei valori interi, esempio verificare se sia 1,2,3,4 ecc.. meglio usare uno switch(), altrimenti usare un if e else if.

La struttura di controllo switch() non è indispensabile ma spesso rende il codice più leggibile.

fabpolli:
Così i due pezzi di codice (che penso siano stati trovati su internet piuttosto che scritti da zero da te) fanno cose differenti poiché il secondo if ha due condizioni in or, cosa che lo switch non ha ovviamente

Mi spiace, ma i due pezzi di codice li ho fatti io di sana pianta, senza copiare nulla...
Comunque mi è chiaro il discorso, quello che volevo sapere mi è stato spiegato molto bene da nid69ita, ho capito che i tempi di esecuzione sono simili e questo mi basta...
Anche l'idea creativa dello switch è davvero...creativa.....
Grazie davvero per le spiegazioni.

Se avessi scritto

switch (  switch (eventi[i]) {
    case 0:
      bitClear(buffer_uscite, i - 6);
      eventi[i]=0;
      break;
    case 1:
      bitSet(buffer_uscite, i - 6);
      break;
    case 2:
      bitClear(buffer_uscite, i - 6);
      eventi[i] = 0;
      break;
  }

E' uguale a quello che ho scritto con l' if?
se si che senso ha scrivere nel case 0 eventi*=0 quando è già 0????*

questo è errato: switch ( switch (eventi[ i ] ) {
questo è corretto: switch ( eventi[ i ] ) {

  1. ovviamente non serve ma mi sono concentrato sul portare paro-paro gli if in switch
    anche la biClear è uguale in tutti e 3 i case, si potrebbe ottimizzare, ma non era l'argomento

Beh, scusa il punto 1 era un chiaro errore di copia incolla che è venuto il doppio switch...
Per il punto 2, nel case 1 è un bitSet e non un bitClear...
Cmq grazie per l'aiuto.

manolomao:
Se avessi scritto ... E' uguale a quello che ho scritto con l' if?

Ni, c'è il discorso del mutuamente esclusivo, notare l'else:

switch (a){
  case 1:
  ...
  break;
  case 2:
  ...
  break;
}

if (a == 1){
  ...
}else if (a == 2){
  ...
}

In molti casi non fa alcuna differenza, in altri si, sono due flussi diversi.

grazie, mi è chiaro...
volevo sottoporvi un altro problema...in questi giorni ho del tempo da usare per apprendere.
Ho bisogno di controllare 6 PWM, e per temporizzare ogni step ho utilizzaro la libreria MsTimer2.
Non ho dubbi su come usarla ma su come migliorare il controllo dello start e dello stop.
In pratica ho delle variabili boolean che mi dicono se il PWM sta salendo o scendendo racchiuse in due array

boolean fade_up[6]
         boolean fade_dw[6]

ora per far partire la libreria il comando da usare è

MsTimer2::start();

mentre per farla disattivare il comando è

MsTimer2::stop();

Quindi mi trovo che se almeno una delle variabili fade_up e fade_dw è vera parte il timer

 if ((fade_up[0])||(fade_up[1])||(fade_up[2])||(fade_up[3])||(fade_up[4])||(fade_up[5])) {MsTimer2::start();}     
 if ((fade_dw[0])||(fade_dw[1])||(fade_dw[2])||(fade_dw[3])||(fade_dw[4])||(fade_dw[5])){MsTimer2::start();}

Se tutte le variabili fade_up e fade_dw sono false il timer si spegne

if ((!fade_up[0])&&(!fade_up[1])&&(!fade_up[2])&&(!fade_up[3])&&(!fade_up[4])&&(!fade_up[5])&& 
     (!fade_dw[0]) &&(!fade_dw[1])&&(!fade_dw[])&&(!fade_dw[3])&&(!fade_dw[4])&&(!fade_dw[5])){MsTimer2::stop();}

Lasciando perdere l'identazione (me ne scuso....), esiste una scrittura migliore per fare quello che voglio fare?
Grazie.

boolean any(boolean a[]) { return a[0] || a[1] || a[2] || a[3] || a[4] || a[5]; }


if (any(fade_up))  { MsTimer2::start(); } 

if (!any(fade_up)) { MsTimer2::stop(); }

Piuttosto (non conosco la libreria) non so se è giusto richiamare continuamente MsTimer start, se serve a riavviare tempo allora ok.

Claudio_FF, si il timer devi avviarlo ogni volta che deve iniziare a contare e poi fermarlo quando non serve più....
Fammi capire

boolean any(boolean a[]) { return a[0] || a[1] || a[2] || a[3] || a[4] || a[5]; }

Questa istruzione precisamente come si comporta??
perchè io ho due array , uno è fade_up e l'altro è fade_dw...
se uno solo è vero parte il timer, se tutti sono falsi ferma il timer.....
Grazie per l'aiuto

manolomao:
perchè io ho due array , uno è fade_up e l'altro è fade_dw...
se uno solo è vero parte il timer, se tutti sono falsi ferma il timer.....

Attenzione che qui le parole sono importantissime:

Se uno solo, oppure se almeno uno? Fa la differenza tra uno XOR e un OR. Il codice che hai scritto rappresenta un OR, quindi "se almeno uno".

Parte il timer, oppure RIparte il timer? Fa la differenza tra un timer singolo impulso e uno retriggerabile.

Non conosco la libreria , ma se MsTimer start fa RIpartire il timer, e non solo partire, allora non capisco a cosa serva, perché il timer sarà sempre in conteggio, per fermarsi solo quando tutti gli elementi degli array saranno falsi. In sostanza, cosa dovrebbe fare quel timer?
Vedo qui al paragrafo Example. Start attiva gli interrupt di MsTimer dopo che in precedenza si è settato il periodo.

Questa istruzione precisamente come si comporta??

È una funzione che restituisce l'OR tra gli elementi dell'array passatole, se tutti gli elementi sono falsi restituisce falso, altrimenti se almeno uno e` true restituisce true. Usando la funzione il tuo codice completo sarebbe:

if (any(fade_up)  ||  any(fade_dw)  { MsTimer2::start(); }  // start se almeno un elemento true
else                                { MsTimer2::stop();  }

Grazie Claudio_FF, effettivamente hai detto bene te, se almeno uno è true parte il timer....
Quindi ricapitolando lmetto

boolean any(boolean fade_up[]) { return fade_up[0] || fade_up[1] || fade_up[2] || fade_up[3] || fade_up[4] || fade_up[5]; }
boolean any(boolean fade_dw[]) { return fade_dw[0] || fade_dw[1] || fade_dw[2] || fade_dw[3] || fade_dw[4] || fade_dw[5]; }

e poi

if (any(fade_up)  ||  any(fade_dw)  { MsTimer2::start(); }  // start se almeno un elemento true
else                                { MsTimer2::stop();  }

Quello che hai scritto a margine non lo capisco....

manolomao:
Grazie Claudio_FF, effettivamente hai detto bene te, se almeno uno è true parte il timer.... Quindi ricapitolando lmetto

boolean any(boolean fade_up[]) { return fade_up[0] || fade_up[1] || fade_up[2] || fade_up[3] || fade_up[4] || fade_up[5]; }

boolean any(boolean fade_dw[]) { return fade_dw[0] || fade_dw[1] || fade_dw[2] || fade_dw[3] || fade_dw[4] || fade_dw[5]; }

No, la funzione 'any' resta come la avevo scritta, è una funzione da chiamare, non un'istruzione.

boolean any(boolean a[])
{
    if (a[0]  ||  a[1]  ||  a[2]  ||  a[3]  ||  a[4]  ||  a[5])
    {
        return true;
    }
    else
    {
        return false;
    }
}

Il discorso a margine era incomprensibile perché mi era saltato un pezzo...
Dicevo che quando devo gestire qualcosa con più fasi, scrivo le fasi esplicitamente, con le condizioni di passaggio da una all'altra, compresi eventuali timeout calcolati con millis:

switch (fase){

    case 30:
        ....
        if (...condizione cambio fase...) { fase = ...; }
    break;

    case 10:
        ....
        if (...condizione cambio fase...) { fase = ...; }
    break;

    case 65:
        ....
        if (millis()-inizio > 10000) { fase = ...; }  // timeout 10s
    break;

}