Newsgroup: it.comp.lang.c++
Titolo: Aritmetica dei puntatori e matrici

Salve a tutti. Spesso uso l'aritmetica dei puntatori per accedere ad
elementi di vettori. E' possibile farlo anche con matrici tipo a[5][5]?
Non trovo questa informazione da nessuna parte.
Grazie mille!

Luca nel messaggio precedente ha scritto:

Per tentativi ci sono arrivato: *(*(a+i)+j)... però a rigor di logica
non mi torna molto, non capisco perché funzioni così. Forse perché
(a+i) è un puntatore che punta al puntatore che punta al primo elemento
della riga i-esima?

elementi di vettori. E' possibile farlo anche con matrici tipo a[5][5]?
T a[m][n];
significa un array di m array di n elementi di tipo T. In C (e C++) un
array decade, a meno di condizioni eccezionali, in puntatore al suo
primo elemento. Quindi scrivere: int a[5] e poi usare *(a+2) significa
far decadere 'a' al puntatore al suo primo elemento (quindi il
puntatore è di tipo int*) e incrementare l'indirizzo ottenuto di
2*sizeof(int) byte per raggiungere l'indirizzo del terzo elemento. Il
tuo 'a' è un array di 5 array di 5 elementi di tipo T (non l'hai
specificato), quindi se chiami S il tipo "array di 5 elementi di tipo
T", ottieni che 'a' è:
S a[5];
ora, scrivere *(a+2) significa far decadere 'a' al puntatore al suo
primo elemento (quindi il puntatore è di tipo S*) e incrementare
l'indirizzo ottenuto di 2*sizeof(S) byte per raggiungere l'indirizzo
del terzo elemento. Quello che ottieni è un elemento di tipo S, cioè a
sua volta un array. Essendo l'elemento un array di 5 elementi di tipo
T puoi riapplicare il ragionamento:
*(*(a+2)+3) 
Fai cioè decadere il nuovo array *(a+2) in puntatore al suo primo
elemento di tipo T, quindi, incrementando l'indirizzo ottenuto di
3*sizeof(T) byte, raggiungi la posizione del quarto elemento di tipo
T.

 Per tentativi ci sono arrivato
Suggerisco di prenderti un buon manuale.
-- 
cc = callcc {|cc| cc.call cc}; cc.call cc

 Suggerisco di prenderti un buon manuale.
Ho parecchi libri (e soprattutto le dispense del mio docente), ma come
ho detto questo non l'ho proprio trovato. Si limitavano a spiegare il
caso semplice di un vettore, che tutto sommato è intuitivo. Grazie
comunque per la risposta di Andrea Laforgia!

 caso semplice di un vettore, che tutto sommato è intuitivo.
Io non ti ho detto prendi molti libri. Ti ho detto prendi *un* *buon*
manuale. Vista la tua domanda, è evidente che i testi su cui studi *non*
sono buoni (o che tu non li studi, nel caso c'è poco da fare). 


comunque per la risposta di Andrea Laforgia!
Prego. ;-)

 degli Errata Corrige, per apprendere cose corrette).
Sulla sua bontà didattica non saprei. Io lo ho letto quando programmavo
C da un po' di tempo e in altro da ancora di più (per cui il pelo sullo
stomaco lo avevo già fatto).
Trovo tuttavia che la parte su puntatori, array, puntatori a puntatori e
correlati sia l'esposizione più lucida che mi sia capitato di leggere.
My 2 cents, eh.


 Fai tanti esercizi.
Come non detto. Il docente ha pubblicato proprio un capitoletto su
questa parte ieri. Eventualmente proverò a procurarmi il K&R, anche se
forse lo avevo. Uno dei libri che ho studiato per intero già parecchi
anni fa è proprio di Herbert Schildt :-).
Esercizi? Io uso C++ da anni :-). Il problema è che ho sempre dovuto
usarlo immediatamente senza avere il tempo di approfondire bene la
parte teorica, ecco perché alla fine mi blocco su banalità. Certo
comunque se ne ho bisogno risolvo in altri modi senza grandi problemi
di solito. Ora vorrei studiarmelo proprio bene.

 sono buoni (o che tu non li studi, nel caso c'è poco da fare).
Come ho detto mancava una parte che è stata pubblicata poi. Purtroppo il
docente pubblica le dispense a pezzi, ed io non capisco questa mania di
farlo. Probabilmente la sta completando ora, non so.
I libri che ho sono quelli che sono suggeriti dalle FAQ del ng. Mi è
parso che questa cosa non ci fosse, ma magari sono solo io che non l'ho
trovata non sapendo dove cercare. Però in effetti forse il problema è
proprio che questa parte è più la parte di C che di ++.
Grazie mille!

 tenerti alla larga da qualunque libro di Herbert Schildt.
Il fatto è che non vorrei imparare cose differenti, studiando da un
libro di C invece che di C++. Perché dici edizione italiana? Se vado in
libreria e chiedo il titolo e gli autori non me lo trovano dici?

 Io uso C++ da anni :-).
E non avevi *mai* capito i puntatori? Argh.
Nota che il concetto è perfettamente scalabile: se sai come funziona un
puntatore, sai anche come funziona un puntatore a puntatore (e iteri con
puntatore a puntatore a puntatore etc).


 addirittura scrivono i compilatori...
Guarda che scrivere un compilatore in se non è molto complicato. Che so,
un compilatore per un subset ragionevole di C lo scrivi in una settimana
o giù di li. Poi aggiungi altri tipi, strutture e puntatori.
Il punto cazzuto è l'ottimizzatore. Quello molto molto molto. 
Poi attenzione, chi scrive un compilatore non necessariamente è chi
progetta un linguaggio.


 iteri con puntatore a puntatore a puntatore etc).
mmh... no, ho sempre usato i puntatori ed i riferimenti, solo che questa
cosa particolare di accedere agli elementi di una matrice l'avevo
sempre fatta tramite subscripting. Al più conoscevo l'accesso al
vettore. Forse ci sarei dovuto arrivare per ragionamento in effetti...

 progetta un linguaggio.
Mi hai tolto le parole di becco... lì ci vuole una buona dose di
creatività secondo me per progettarlo.

 creativitą secondo me per progettarlo.
Diciamo che ti ci vuole un problema da risolvere, soprattutto. 


 vettore. Forse ci sarei dovuto arrivare per ragionamento in effetti...
Guarda che usare subscripting e la notazione esplicita è *esattamente*
la stessa cosa da un punto di vista semantico. Lo standard dice chiaro e
tondo che a[b] è convertibile in *(a+b) (over tutti i tipi siano a
posto, ovvero uno fra a e b sia un puntatore non void* e l'altro sia un
intero). Trascuro discorsi di 'starci dentro e compagnia' che non sono
rilevanti in questo contesto.


 sono rilevanti in questo contesto.
Chiaro che è la stessa cosa, ma sai, lo subscripting è molto intuitivo,
io che non avevo capito a fondo l'aritmetica dei puntatori conoscevo
*(a+b) perché l'avevo letto ma l'altro mi ci è voluto un poco per
capirlo.

 Chiaro che è la stessa cosa, ma sai, lo subscripting è molto intuitivo,
E' vero. E' molto leggibile e intuitivo.


 puntatore non void* e l'altro sia un intero).
Peraltro
int a = 5;
int *b = ...;
quanto detto fa si che
a[b] 
sia un accesso valido.
Il che mi ha sempre lasciato un po' perplesso :)
--*PaN!*
 
-- 
I'm using an evaluation license of nemo since 160 days.
You should really try it!
http://www.malcom-mac.com/nemo

 Il che mi ha sempre lasciato un po' perplesso :)
Anche a me pareva chissà quale magheggio. Poi ho dovuto maneggiare una
grammatica del C e in effetti è praticamente inevitabile. :)
Cioè, proibirlo avrebbe complicato grandemente le cose, IMHO.


  Cioè, proibirlo avrebbe complicato grandemente le cose, IMHO.
Se avessero anche ristretto *(a+b) ad avere a sinistra dell'operatore
solo puntatori, non avrebbero avuto grandi problemi IMO (e, nota,
sarebbe stata un ulteriore semplificazione :))
Mi sorprende che, con il taglio netto introdotto da C++ sulla
tipizzazione dei puntatori, non si sia deciso di tagliare anche questa
(pero' e' divertente per lasciare a bocca aperta gli studenti, e
successivamente minacciarli di morte atroce se proveranno mai ad
utilizzarlo in un esercizio/esame :)


 a[b] 
Dunque anche
7[b]
è valido? Curioso... Ma pensandoci mi sa di averlo visto usare in 
qualche gara di codice oscuro.
E.

 č valido? 
        printf("%ces\n", 1["N"]["Y"]);
Max

         printf("%ces\n", 1["N"]["Y"]);
Ugh! Ci ho messo un po' a capirlo... Però se ci metti 0 invece di 1 non 
ti esce la N!
E.

 correlati sia l'esposizione più lucida che mi sia capitato di leggere.
Io invece trovo che chi progetta i linguaggi, quasi mai è capace di
spiegarne chiaramente i meccanismi :-)
Non reputo il K&R il miglior manuale per imparare il C, tanto quanto
non considero il Stroustrup il migliore per il C++.
Ovvio, poi, che bisogna possederli entrambi.
Se si vuole un bel tutorial su array e puntatori, tempo fa ho visto
quello di Ted Jensen: 
http://pw1.netcom.com/~tjensen/ptr/pointers.htm

 Esercizi? Io uso C++ da anni :-).
No comment.

 No comment.
Si, diciamo che io più che di esercizi avrei bisogno proprio di uno
studio ordinato della teoria, ed ovviamente di esercizi su quello che
non ho mai fatto in caso. Anche perché ho sempre scritto ad oggetti.

 sarebbe stata un ulteriore semplificazione :))
Dovrei provare ad implementarmela un attimo. Ma a sboccio mi è sembrato
che dovrei complicare il codice. Ripensandoci a livello banale
probabilmente no. Ma mi sa che mi sfugge qualcosa.
Comunque no, per la grammatica non sarebbe stata *certamente* una
semplificazione. Inoltre ci sarebbe stato un vincolo in più per i
controlli di semantica statica.


 ti esce la N!
Per forza :)
1 va a prendere lo \0 che poi va a prendere la Y.
Se metti 1 va a prendere la N, che va a prendere l'n-ascii-esimo valore
in "Y", che ovviamente è errore. Purtroppo non è facilmente estendibile
in modo che vada a prendere la N, poichè la N è sempre usata come valore
numerico per prendere l'offset del 'secondo subscript'.


 quello di Ted Jensen: 
http://pw1.netcom.com/~tjensen/ptr/pointers.htm
Magari ci darò un occhio. :)


 numerico per prendere l'offset del 'secondo subscript'.
No, ma il costrutto piů semplice:
"NY"[i]
potrebbe in effetti essere utile... non avevo mai pensato che si potesse 
  usare...
E.

   usare...
Si, si può usare. :)


 Max
ahem ... inutile che mi ci sforzi ulteriormente, ma non ho 
capito come evolve l'interpretazione di quella espressione
1["N"]["Y"]
Curiosamente sono riuscito a comprendere l'esempio tradotto da 
MAnu?, ossia
"NY"[0]  oppure  "Pippo"[3]
(anche se non pensavo fossero validi, li trovo fantastici !
E immagino che funga anche con un indice numerico variabile, tipo :
int Index = Calculate ();
char C = "Codice Carattere" [Index];
questo è quel che intendevate ?
Ma cmq l'espressione originale con due subscript non l'ho capita
Penso (non sono sicuro) che  2["NOT"]  equivalga a   "NOT"[2]
ossia al carattere 'T' .... vero ?
Ma il doppio subscript che cavolo di dato produce ? Come si fa a 
dereferenziare due volte un semplice puntatore a carattere ?
attendo lumi pls
ciao
Soviet

 attendo lumi pls
Attenzione! Non è un semplice puntatore a carattere.
Prima di tutto accordiamoci sul fatto che 
a[b] è tradotto dal compilatore in *(a+b) (posto che i tipi siano a
posto, questa è una condizione che darò scontata).
A questo punto è completamente evidente che a[b] == b[a]
Veniamo ad:
a[b][c] -> (a[b])[c] 
        -> (*(a+b))[c]
        -> *(*(a+b) + c)
Ecco, ora aggiungiamo i tipi:
1["N"]["Y"]
     |
     V
*(*(1 + "N") + "Y")
Cosa è "N" + 0? E' l'indirizzo al carattere 'N'. Cosa è "N" + 1? E'
l'indirizzo del carattere '\0' che termina l'array di lunghezza 2 "N",
ovvero {'N', '\0'}.
Per questo motivo, "N"[1] è '\0', ovvero 0.
Che usiamo come indice per il secondo coso. Per cui otteniamo 'Y'.


 Ma il doppio subscript che cavolo di dato produce ?
1["N"] -> 0
0["Y"] -> Y
EMG
--
EMAG Software Homepage:     
http://www.emagsoftware.it
The EMG's ZX-Spectrum Page: 
http://www.emagsoftware.it/spectrum
The Best of Spectrum Games: 
http://www.emagsoftware.it/tbosg
The EMG Music page:         
http://www.emagsoftware.it/emgmusic
 

 
azz ... così semplice ? Che figura.
il primo step produce zero (da usare come offset per la seconda 
indirezione), perché conti sul null finale della stringa literal 
"N", vero ?
Cmq, si, penso di aver capito la logica.
Mi resta da chiarirmi come si concilia questa cascata di 
collassamenti di tipo con la dimensionalità delle matrici (o 
meglio coi controlli che il compilatore può fare per dirti se 
stai usando una matrice X dimensionale con Y indici).
Forse in effetti il C++ non ha la nozione di matrice 
multidimensionale, ma queste entità sono trattate come aggregati 
(array di array di array).
Cmq di sicuro qualcosa non mi è chiaro in merito, ma non so 
ancora bene cosa sia, per cui soprassiedo un po'
ciao
Soviet
Ah, grazie


crxor 666 ha scritto:

grazie anche a te.
LA cosa orrida, imho, della scrittura
int[stringa], a parte il fatto di essere legale, è che 
suggerisce uan leggibilità diversa, tipo una sorta di array 
associativo indicizzato a keyword stringa. Cmq, l'esempio ora mi 
è chiaro
ciao
Soviet

 (array di array di array).
Più semplice ancora: puntatore di puntatore di puntatore etc. E il
compilatore non sa molto altro (lascio perdere per semplicità i veri
array multidimensionali, tipi completi, incompleti etc etc etc). 
Comunque no, il C++ non ha la nozione di 'matrice'. Logicamente in C++
per avere una matrice ti fai una bella classe che se la smazza. E' di
una semplicità impressionante, può essere molto efficiente e uno se la
gestisce come vuole.
Tipicamente ad uno che vuole fare specialmente del calcolo fa comodo una
matrice templatica con le dimensioni come parametri del template. In
questo modo le operazioni fra matrici sono dimensionalmente corrette a
livello di compilatore.
Poi che so... se uno invece sta lavorando semplicemente di geometria
nello spazio potrebbe avere matrici in cui i dati stanno proprio sullo
stack(tipicamente avrà vettori di 3 elementi, magari per come la
gestisce ne crea e ne distrugge molti e o così oppure un pool
specializzato che non passa troppo spesso per allocazione vera di
memoria).
A questo punto tensori[0] del secondo ordine simmetrici e antisimmetrici
possono essere rappresentati con meno di 9 elementi: da cui potrebbe
avere senso farli rispettivamente da 6 e da 3 valori. Etc etc etc.
Insomma, un tipo di dato 'matrice' in un linguaggio come C++ non lo
vedrei benissimo.
Ovviamente il tutto si complica se pensiamo che possiamo avere matrici
di n dimensioni. Gestire tutto in modo sufficientemente generico e
sufficientemente efficiente non è possibile oppure è roba da spenderci
parecchio in ricerca. Quindi C++ dice: arrangiati.
----
[0] 
http://it.wikipedia.org/wiki/Tensore
 
http://it.wikipedia.org/wiki/Calcolo_tensoriale
        : ... (temo che per chi non sappia cosa sia un tensore i 
               suddetti link siano virtualmente incomprensibili)


 associativo indicizzato a keyword stringa. 
Eh, invece è una mera questione di grammatica. :)
Si, anche io la prima volta che lo ho visto pensavo fosse un trucco.


  icontrolli di semantica statica.
Attualmente deve controllare che una delle due espressioni sia di tipo
puntatore, e l'altra delle due sia di tipo intero. Io avrei
semplicemente controllato che *la prima* fosse di tipo puntatore e *la
seconda* di tipo intero.
A semtimento mi pare che le cose mi pare si semplifichino (ma anche se
si fossero complicate, non credo che K&R non sarebbero stati in grado
di affrontare la complessita' della situazione :))... 
--*PaN!*
 
-- 
I'm using an evaluation license of nemo since 163 days.


 di affrontare la complessita' della situazione :))... 
Non è mai questione di 'non essere in grado'. La complessità (non
necessaria) è sempre bene evitarla. BTW, nota che tutto il C è
improntato all'idea della semplicità (di progetto) e dall'idea che il
programmatore è perfettamente libero di farsi male in tutti i modi che
reputa opportuno. Sicuramente vietare una sintassi insolita non è al
centro della loro visione, ecco! :)
