Una funzione per variare dei valori tramite i puntatori.

Ciao forum,
Con la mia poca esperienza della programmazione in C e di arduino, vorrei tentare una funzione che alla pressione di due tasti cambia il valore della variabile che dovrebbe essere passata alla funzione dal suo puntatore.
Al momento ho il problema che se passo un byte non lo accetta impostando la funzione come int, ed anche viceversa.
La funzione dovrebbe essere di questo genere (con errori annessi)

#include <Button.h>
// Button library from https://github.com/tigoe/Button/blob/master/Button.cpp

#define DEBUG

// create the buttons
Button menu = Button(12,BUTTON_PULLDOWN);
Button select = Button(13,BUTTON_PULLDOWN);
Button plus = Button(14,BUTTON_PULLDOWN);
Button minus = Button(15,BUTTON_PULLDOWN);

uint8_t myval = 50;

void changeValue(uint16_t minval, uint16_t maxval, &value)
{
    if (minus.uniquePress()) {
        if (value > minval) --value;
    }
    if (plus.uniquePress()) {
        if (value < maxval) ++value;
    }
    // queste funzioni non mi sembra fanno quello che vorrei
    if (minus.heldFor(1200)) {
        if (value >minval + 10) value -= 10;
    }
    // queste funzioni non mi sembra fanno quello che vorrei
    if (plus.heldFor(1200)) {
        if (value< maxval - 10) value += 10;
    }
}

void setup()
{
    #ifdef DEBUG
    Serial.begin(57600);
    while (!Serial) {;}
    Serial.println("System Ready");
    #endif
}

void loop()
{
    while (1) {
        Serial.print("Value  =");
        Serial.println(myval);
        delay(400);
        changeValue(0, 100, *myval);
        if (select.uniquePress()) break;
    }
}

È facile che non ho ancora capito come funziona.
Se lo faccio con un valore di ritorno, allora dovrebbe funzionare. Del tipo

uint16_t changeValue(uint16_t minval, uint16_t maxval, uint16_t value)
{
    // bla bla
    return vaule;
}

myval = changeValue(0,100, myval);

Con un aumento di consumi di memoria…

Sei in C++, usa le reference:

void changeValue(const uint16_t minval, const uint16_t maxval, uint16_t& value) {
  //...

Sicuro di quello che hai postato ?
void changeValue(uint16_t minval, uint16_t maxval, ?? &value)
qui manca la dichiarazione del tipo di value. E sembra che vuoi usare i reference (come proposto da @sukko )
Ma poi nella chiamata usi asterisco: changeValue(0, 100, *myval);
Un pò un pasticcio, mischiando tecniche diverse.

Se usi i puntatori puri, usi int e se passi un byte fai un cast.

uint8_t myval = 50;
void changeValue(uint16_t minval, uint16_t maxval, uint16_t *value)
{ if( *value<maxval) (*value) += 10;
  else               (*value)=minval;
}
void setup(){  Serial.begin(9600); }
void loop()
{  Serial.print("Value  =");
   Serial.println(myval);
   delay(400);
   changeValue(0, 100, (uint16_t*)myval);
}

Se usi reference, anche qui cast:

uint8_t myval = 50;
void changeValue(uint16_t minval, uint16_t maxval, uint16_t &value)
{ if( value<maxval) value += 10;
  else               value=minval;
}
void setup(){  Serial.begin(9600); }
void loop()
{  Serial.print("Value  =");
   Serial.println(myval);
   delay(400);
   changeValue(0, 100, (uint16_t&)myval);
}

Però essendo in C++, in teoria, sarebbe meglio usare overload della funzione, ovvero fai due versioni, una per gli uint16_t e una per gli uint8_t

nid69ita:
Sicuro di quello che hai postato ?

Sono sicuro che non so come fare :slight_smile:
Avevo pensato anch' io di usare due versioni della funzione, sebbene mi sembra un po dispendioso.
Poi la compilazione è riferita ad Arduino, solo che un programma, non la IDE, vuole attenersi allo standard ISO.

Beh con i reference è più pulito se usi i tipi uguali, se poi devi passare un tipo più piccolo, il cast funziona, solo che non è molto elegante in C++. Ma siccome su queste piccole MCU bisogna risparmiare i byte... amen l'eleganza :slight_smile:

Ho notato che l' ampersand, sukko l' ha messo attaccato all' uint16_t. È un errore, oppure si può fare cos'?

Lo puoi mettere dove vuoi.
Come lo ha scritto @Sukko mi pare più logico, visto che quel & indica reference e mi pare più logico attaccarlo all'altro attributo del parametro, ovvero il tipo.

Forse è bene un riepilogo:

Puntatori, C-Style, ma perfettamente corretto anche in C++:

void increment (int *x) {
  *x += 1;
}

int a;
increment (&a);

Reference, solo C++:

void increment (int& x) {
  x += 1;
}

int a;
increment (a);

Queste due cose sono equivalenti. Nota bene la sintassi dei parametri, dell'uso di questi e delle chiamate.

La questione di attaccare l'ampersand o l'asterisco al tipo o alla variabile è unicamente stilistica. Solitamente in C si attacca alla variabile, mentre in C++ al tipo, ma non cambia assolutamente niente.

Mi pare chiaro che il puntatore è int (o la grandezza di un indirizzo di memoria). Mentre il tipo della variabile bisogna definirlo per le operazioni del calcolo, vero ?
Ma l' int del puntatore si riferisce alla grandezza valore puntato ? Per il quale poi si faranno i calcoli ?

Per spiegarmi, se incremento un byte, in assembly è un certo tipo di opcode, ma se incremento una word o long, gli opcode in assembly sono diversi. Quindi non ho il privilegio di assentarmi a dare un cast alla variabile, ovvio. Sperando che poi il compilatore mi trovi la soluzione ad ogni caso di variabile passata.
Poi se voglio incrementare il puntatore, l' operazione mi torna complessa. :o :o

Ragionamento, per farvi vedere quello che capisco.
C'è un certo dubbio su cosa scegliere, per avere la funzione che valga per la maggior parte dei casi, non posso lesinare di passargli dei bytes, se poi la voglio usare anche con delle int.
Allora il conto della spesa, dovrò allargarmi sulle int.

ExperimentUno:
Mi pare chiaro che il puntatore è int (o la grandezza di un indirizzo di memoria). Mentre il tipo della variabile bisogna definirlo per le operazioni del calcolo, vero ?
Ma l' int del puntatore si riferisce alla grandezza valore puntato ? Per il quale poi si faranno i calcoli ?

Si, serve quando fai ad esempio x= *valore; il compilatore deve sapere quanta memoria leggere. Per i calcoli o anche solo per leggere/scrivere.
Ovvero valore contiene un puntatore, e tutti i puntatori sono uguali come dimensione (sono indirizzi di memoria e quindi tutti della stessa dimensione) ma cambia il "cosa" punta ovvero alla dimensione di ciò a cui punti.
L'uso di puntatori castati, devi fare molta attenzione, infatti può essere utile per leggere i singoli byte di un float. Ma è anche "pericoloso" se usato in malo modo. Puoi scrivere "fuori" dalla memoria "lecita".

Ancora grazie per la squisita lezione.
Una piccola domanda, come si aumenta/ calcola un puntatore in C++Style.
Ho letto che si fa un dereferenza con *puntatore, poi posso incrementare con (*puntatore)++;. A quel punto non c'è più la referenza di prima, cosa faccio per rimetterla ?
Ancora &puntatore ?
Credo che col C-style invece si toglie l' asterisco per operare ai puntatori e lo si rimette per operare all' indirizzo puntato.

Scusate, non è una trollaggine, sto cercando di assimilare concetti nuovi...

Per un puntatore puro, la variabile contiene un indirizzo. Se lavori sulla variabile alteri l'indirizzo. Se usi * lavori su quello a cui punta l'indirizzo.
I puntatori nascono con il C e si usano anche in C++. Poi in C++ hai anche i reference. Ma non esclude che in C++ non usi più i puntatori come si fa col C. Hai solo una funzionalità in più.
Anche in C++ hai le stringhe classiche (null terminated) e li si continua a lavorare molto con puntatori (essendo array/vettori e non singole variabili).

Per il reference... secondo me hai letto male in internet. Ci sono pagine che parlano di "reference and deference" ma parlano di puntatori.
Un reference non ha, come un puntatore, due punti di vista, il valore indirizzo e il cosa punta.
Un reference ha solo la vista "a cosa punta".
Un puntatore può anche essere dichiarato e non inizializzato subito.

sezione "Relationship to pointers"
Un reference deve essere inizializzato.