Creare Librerie

i puntatori puntando ad un indirizzo (o aria di memoria) quindi il termine non è errato

lesto:
i puntatori puntando ad un indirizzo (o aria di memoria) quindi il termine non è errato

Ho letto la guida, ma tra l'altro già ci avevo dato un'occhiata tempo fa e infatti cosa sono i puntatori ce l'ho più o meno chiaro, ora ho chiaro anche quale sia la differenza tra & e *:

  • serve per memorizzare il valore della variabile puntata
    & serve per memorizzare l'indirizzo della variabile puntata

quello che non riesco a immaginare è cosa succeda passando a una funzione l'uno e l'altro.

Cioè nella guida fa l'esempio di una funzione (swap) che richiede due *puntatori e vengon a passati due &parametri
quindi crea di fatto un puntatore (con *) e io passo l'indirizzo (con &). Giusto?
Su questo non mi è chiarissimo, ma diciamo che ha la sua logica e credo si tratti di fare esperienza.

Io però ho un codice dove il codice della funzione chiede direttamente dei parametri con &, in questo caso cosa fa?

Ad esempio viid funzione (chat pinco, unsigned int &palla){...};

Cosa fa? memorizza solo l'indirizzo del parametro che gli passerò?

e quando dentro alla funzione poi vedo "palla = x+y;" cosa sta facendo? nel senso che io ora in palla ho il valore dell'indirizzo a quanto ho capito dalla guida...

Cioè a me non è che non sia chiaro cos'è un puntatore, piuttosto non capisco cosa succede quando passo dei parametri in questo modo a una funzione :blush:

Hai visto il libro che ti ho indicato poco sopra ??? Perché li c'è di tutto e di più ! ]:smiley:

Guglielmo

lesto:

Il C++ ha introdotto

??? il passaggio di puntatori come parametri c'è anche in C

Certo, ciò che non c'è in C ANSI è il passaggio di parametri per rifermento o alias, cioè la seguente riga
è un errore:

myFunc(const int &nameVar) {

}

Mentre è valida per C++.

  • serve per memorizzare il valore della variabile puntata
    & serve per memorizzare l'indirizzo della variabile puntata

La confusione come ho già scritto prima deriva dal fatto che * e & svolgono compiti differenti in base a come e dove vengono scritti. & Ricava l'indirizzo di una variabile, oppure se usato nella lista dei parametri specifica il passaggio per riferimento o alias, che ti permette di accedere alla variabile da dentro la funzione.

La stessa cosa si può fare con i puntatori, ma per accedere alla variabile puntata devi anteporre *, operatore di risoluzione.

La cosa è molto semplice, quasi banale, tuttavia io compreso mi confondevo.

Come faccio a ricavare l'indirizzo di una variabile?
Uso l'operatore &. es: int *intPtr = &intVar;
Da destra verso sinistra: intVar è una variabile da cui ricavo l'indirizzo che assegno a intPtr che è un puntatore a tipo intero. PS: intPtr è anche una variabile per cui posso ricavare l'indirizzo di intPtr: &intPtr

Come faccio a passare un argomento per riferimento?
Uso l'operatore &

Da dentro la funzione come uso l'argomento passato per riferimento?
Nel modo normale di usare una variabile.

Come ricavo il valore da una variabile puntatore?
Uso * prima della variabile puntatore, per ricavare (leggere) il contenuto della cella di memoria.

Una cella di memoria ha due proprietà:

  1. L'indirizzo della cella
  2. Il dato conservato nella cella.

Ciao.

mastraa:

  • serve per memorizzare il valore della variabile puntata
    & serve per memorizzare l'indirizzo della variabile puntata

non si "scrivere", ma di "usare". In oltre sfortunatamente chi ha scritto il C ha deciso di usare il * anche per la dichiarazione

Certo, ciò che non c'è in C ANSI è il passaggio di parametri per rifermento o alias, cioè la seguente riga

oddio hanno spostato la & dalla chiamata alla funzione... cosa brutta secondo me, non ti accorgi se la tua variabile potrebbe essere modificata dalla funzione.. altri vantaggi?

@lesto
Puoi impedire alla funzione di modificare una variabile passata per riferimento o alias tramite il qualificatore "const", stessa cosa per il passaggio di una variabile puntatore. Questo però l'ho già scritto nei post precedenti. 8)

Ciao.

Il libro della O'Reilly lo sto aprendo ora che ho un computer disponibile e lo guardo!

Grazie Mauro, mi hai chiarito diversi aspetti, provo a riassumere per vedere se ci sono:

&var ricava l'indirizzo della variabile
io posso dichiarare *pointer (ferma restando la compatibilità tra tipi di variabili)
a questo punto posso assegnare a pointer l'indirizzo di var: pointer=&var
e quindi leggere var anche per mezzo di *pointer.

Nelle funzioni, nei parametri da passare:

'chiedere' &value permette di utilizzare value come se fosse la mia variabile che passo, quindi modificando value modifico anche la mia variabile. Però non posso leggerla perché ne ho solo l'indirizzo, se non tramite il 'giochetto' di prima passando per un puntatore.
Se ho necessita di leggere e modificare la richiedo tramite *value?

'chiedere' &value permette di utilizzare value come se fosse la mia variabile che passo, quindi modificando value modifico anche la mia variabile. Però non posso leggerla perché ne ho solo l'indirizzo, se non tramite il 'giochetto' di prima passando per un puntatore.
Se ho necessita di leggere e modificare la richiedo tramite *value?

Non è chiaro, fai un esempio di codice, così evitiamo fraintendimenti.
Il termine 'chiedere' &value, non è comprensibile.

Vuoi potere accedere ad una variabile da dentro una funzione nello stesso modo in cui la usi da fuori la funzione, ma senza usare puntatori? Nella lista di parametri della funzione usa & prima del nome della variabile, da dentro la funzione la userai nel modo consueto. Questo ovviamente modifica la variabile, in quanto non viene fatta copia locale come per il passaggio per valore.

ops ho dimenticato di chiarire una cosa.

Nella dichiarazione della funzione, i parametri sono da considerarsi essi stessi una dichiarazione, per cui
int &myAlias è una dichiarazione, che potrebbe anche essere detta in questi termini:
hei tu C myAlias è un argomento da passare per riferimento, capito!!
Mentre quando ricavi l'indirizzo di una variabile tramite &, l'indirizzo deve essere ricavato a tempo di esecuzione, qua la cosa si complica un pò a causa della allocazione dinamica della memoria, comunque il compilatore se conosce l'indirizzo di una variabile durante la compilazione risolve in modo statico, diversamente ricava il valore a tempo di esecuzione, cioè durante l'esecuzione del programma.

Ciao.

Vorrei capire la differenza tra le due funzioni, nel senso che entrambe vanno a modificare la variabile che io passo nella posizione di value(s).

Dopodichè in alcuni esempi (anche nei link che mi sono stati postati prima) vedo che quando ha l'* poi nel richiamarla devo usare & (ma qui ad esempio non lo fa, la seconda ad un certo punto richiama la prima).

Rimango 'ingannato' dal fatto che l'esito sembra lo stesso, ma sicuramente c'è un motivo perché si usa uno o l'altro modo.

char SFE_BMP180::readBytes(unsigned char *values, char length)
// Read an array of bytes from device
// values: external array to hold data. Put starting register in values[0].
// length: number of bytes to read
{
	char x;

	Wire.beginTransmission(BMP180_ADDR);
	Wire.write(values[0]);
	_error = Wire.endTransmission();
	if (_error == 0)
	{
		Wire.requestFrom(BMP180_ADDR,length);
		while(Wire.available() != length) ; // wait until bytes are ready
		for(x=0;x<length;x++)
		{
			values[x] = Wire.read();
		}
		return(1);
	}
	return(0);
}

char SFE_BMP180::readUInt(char address, unsigned int &value)
// Read an unsigned integer (two bytes) from device
// address: register to start reading (plus subsequent register)
// value: external variable to store data (function modifies value)
{
	unsigned char data[2];

	data[0] = address;
	if (readBytes(data,2))
	{
		value = (((unsigned int)data[0]<<8)|(unsigned int)data[1]);
		return(1);
	}
	value = 0;
	return(0);
}

quando hai un puntatore, allora devi passre un undirizzo. Una cosa strana sono gli array; essi infatti sono dei puntatori sotto mentite spoglie! quindi NON hanno necessità di mettere lo & davanti, anzi, se usi il nome dell'array con il * è come fare [0] (leggi il primo elemento).

Devi studiare bene quelle guide, i puntatori non si imparano dall'oggi al domani, ti mancano puntaori a funzioni, puntatori doppi (e così via), puntatori void, puntatori a strutture, liste dinamiche, e un intero mondo.

@lock
Anche in asm puoi usare una cella di memoria al cui interno ci scrivi un indirizzo di memoria, che equivale a creare puntatori, ma in quel caso ricavi il contenuto della prima cella e lo usi per accedere alla cella di memoria che risiede a quell'indirizzo. Quindi non credo che si tratti di C, C++ o pascal o modula ecc, semplicemente la ram è fatta così. Ma poi che mi fai scrivere che lo sai meglio di me :stuck_out_tongue_closed_eyes:

Rimango 'ingannato' dal fatto che l'esito sembra lo stesso, ma sicuramente c'è un motivo perché si usa uno o l'altro modo.

Nella tua testa c'è una grande confusione, che diminuirà solo dopo che sperimenti in prima persona come funzionano il C/C++, e l'unico modo è quello di usare un compilatore per PC o (IDE). Crei alcune variabili e stampi i loro indirizzo, crei funzioni e passi i parametri in tutti i modi conosciuti, nessuna variante esclusa. Sperimentare con il C/C++ su un dispositivo embedded non è pratico nè comodo, se ti convinci di ciò ti renderai conto come stanno le cose e alla fine considererai anche banale l'argomento che comunque non mancherà di fregarti quando meno te lo aspetti.

readBytes prende un puntatore ad tipo unsigned char, quindi il puntatore sarà grande 16 bit ma la cella a cui punta è grande 8 bit. Nella chiamata puoi passare un puntatore o un array (vedi cosa dice lesto).

Nel caso specifico di readBytes la funzione è pensata per gestire l'array passatogli attraverso il puntatore "values", infatti il parametro seguente si chiama length, cioè lunghezza di che ? dell'array.

readUInt prende due argomenti, il primo passato per valore e il secondo passato per riferimento.

Se vuoi puoi scrivere dei pezzi di programma piccoli a cui aggiungi i commenti (chiari chiari) che poi noi qui li correggiamo.

char *charPtr; // Un puntatore non ha segno quindi unsigned char al posto di char, ma non è un obbligo
charPtr prende 2 celle di memoria in quanto il puntatore in AVR è grande 16 bit.
Il dato a cui punta invece è grande 8 bit (lo specifica char).
Per ricavare il dato (valore) a cui punta charPtr si antepone *, es: char myChar = *charPtr; // ricavo il valore e lo salvo in myChar, il valore può avere segno negativo o positivo.

charPtr++; incrementa il puntatore di 1 cella, questo mi da accesso alla cella seguente charPtr.
*(charPtr++) incremento e ricavo il valore.

Sono un poco maligno perché voglio che ti convinci a sperimentare il C/C++ su un pc, sarò ancora più maligno
(se prima non lo fa lesto) la prossima volta con i puntatori a puntatori (che mi procurano mal di testa).

Ciao.

@lock: guarda, a ta punto potrei salvarti un saccod i mal di testa con codesto programma, che interpreta il codice in modo matematico e becca tutti gli undefined behaviur

https://code.google.com/p/c-semantics/

edit: ah occhio che si sono spostati su GitHub - kframework/c-semantics: Semantics of C in K

Torno a sfruttare questo topic con un'altra domanda per la libreria che finalmente ho trovato il tempo di scrivere!

Sono arrivato al punto in cui ho bisogno di sfruttare una libreria trovata in giro dentro la mia. Mi spiego, devo usare la libreria OneWire (le sue funzioni) all'interno della mia classe DS18B20. So che esiste già la DallasTemperature, ma per questioni di spazio e velocità ho bisogno di crearmi la mia versione.

Ora ho letto che si può fare qualcosa del tipo class DS18B20: public OneWire, ma non trovo degli esempi con cui capire bene la sintassi e come funziona la cosa.

Qualcuno mi può aiutare?

Ho capito il mio problema, ma non ho capito come risolverlo (sempre che sia una cosa ammessa in C):

per dichiarare una classe derivata di un'altra è sufficiente scrivere

class miaClasseDerivata: public miaClassePrincipale {...}

il mio problema deriva qualora miaClassePrincipale richiedesse dei parametri nel costruttore (come appunto OneWire)

A parte che stiamo parlando di C++ e non di C, non sono ferratissimo sull'argomento ma credo che tu abbia sbagliato qualcosa nel tuo codice (che non fornisci per cui vado ad intuito).
Leggi prima qui:
http://www.learncpp.com/cpp-tutorial/114-constructors-and-initialization-of-derived-classes/

alla fine sono riuscito a risolvere grazie anche all'aiuto di un amico più esperto. In pratica in tutti i tutoria che avevo trovato facevano sempre esempi di classi in cui il costruttore non richiedeva parametri. In tal caso (benché consigliato) non è obbligatorio richiamare il costruttore della principale dentro a quello della derivata. Cosa che invece è necessaria avendo costruttori che richiedono parametri nella classe principale (come appunto OneWire).

Torno nuovamente a porre una questione, sto lavorando con la solita libreria e ora mi viene un errore che non capisco, ho già affrontato diverse volte la classe derivata da un altra, ma ora mi da un errore che non capisco.

class LCD_CLASSIC: public LiquidCrystal
{//riga 133!!!
public:
    LCD_CLASSIC(byte rs, byte enable, byte d4, byte d5, byte d6, byte d7);
    void start();
private:
    byte _col;
    byte _row;
};

Questo è il codice che ho scritto e mi dice che:

In file included from MPU6050.ino:4:
/Users/mastraa/Documents/Arduino/libraries/Mille/Mille_UNO.h:134: error: expected class-name before '{' token

alla riga 4 inserisco la mia libreria.
dentro la libreria ho incluso LiquidCrystal.h così come ho fatto per la Wire.h e anche la LiquidCrystal_I2C.h...

dove sbaglio?

Pare che ti sia scordato una parentesi.
Allega tutto il codice che usi (file .h, file .cpp e file .ino).

Era una cosa ancora più stupida, forse non devo più mettermi alle 23 a fare ste robe!

Semplicemente avevo incluso LiquidCrystal dappertutto, meno che nello sketch di Arduino :cold_sweat:

PS: ma il problema ancora più grosso è che mi sono sbagliato stanotte con la soluzione :astonished:

se estendila classe onewire è perchè vuoi aggiungerene/modificarne le funzionalità. se vuoi solo usarla allora la usi e basta come fai conla Serial (che però è statica) o la Servo.

se apri la libreria DSblabla vedrai che è una semplice classe che USA la OneWire per fare quello che vuoi fare tu: quindi la libreria stessa che vuoi sostituire è la tua base di partenza!