Newsgroup: it.comp.lang.c
Titolo: while, priorita' delle operazioni

Salve, vi chiedo: se abbiamo un'istruzione del tipo
 
while(*s++);
 
dove *s e' un puntatore a char quale sara' l'esatta sequenza
delle operazioni che vengono effettuate all'interno del while?
 
Cioe' se immaginiamo che il puntatore s punti ad un'area di memoria
avente indirizzo FF68, nell'eseguire il while, quale sequenza avrò?
 
1) deferenziazione di s posto in FF68
2) verifica se il valore posto in FF68 e' diverso da zero
3) spostamento del puntatore s in FF69
Il passo 3 verra' eseguito indipendentemente dall'esito del confronto
di cui al punto 2?
 
Oppure avro'
 
1) spostamento del puntatore s in FF69
2) deferenziazione di s posto in FF69
3) verifica del valore posto in FF69
 
Invece, nell'istruzione:
 
while(*s++ = *t++)
 
con *s e *t puntatori a char, quale sara' la sequenza delle
operazioni?
 
1) indirezione di t
2) assegnamento del valore deferenziato da t nell'area di memoria
puntata da s
3) verifica se il valore puntato da t e' diverso da zero
4) avanzamento dei puntatori s e t (con quale ordine?)

 delle operazioni che vengono effettuate all'interno del while?
 
Le priorità degli operatori e le norme per dirimere i casi con pari 
priorità sono quelle spiegate qui:
http://tinyurl.com/ff4pp
 
 
Nello specifico incremento postfisso ha precedenza su dereference. Ed 
entrambi hanno precedenza su assegnamento.
 
-- 
r--r--r--
 


Sicuro? Io vedo, dalla tabella che hai riportato, e anche da quella
nel Kernighan e Ritchie, che l'incremento postfisso e il dereference
hanno la stessa priorità (ambedue 2). E facendo delle prove mi sembra
che il solo puntatore venga incrementato e non il suo valore, ma forse
mi sbaglio io...

 mi sbaglio io...
 
Quando due operatori hanno la stessa precedenza si vede l'associatività (così
come fai con i normali operatori aritmetici, del resto).
In ogni caso se hai dubbi metti le parentesi, non le paghi mica! ;)
Ciao!

 mi sbaglio io...
 
Anche se fossero a pari prioritï¿½ varrebbe questo asserto, dove fa 
l'esempio del prefisso:
 
For example, ++x*3 is ambiguous without some precedence rule(s). The 
precedence table tells us that: x is 'bound' more tightly to ++ than to 
*, so that whatever ++ does (now or laterï¿½see below), it does it ONLY to 
x (and not to x*3); it is equivalent to (++x, x*3).
 
Ma definitiva l'unico modo che hai per essere sicuro ï¿½ consultare il 
manuale del compilatore e controllare come l'hanno implementato.
 


 
infatti... non ricordavo avessero stessa precedenza (avrei
scommesso sul puntatore) ma io metto le parentesi ovunque,
se non altro per ricordare a prima vista cosa volevo fare.
 
Un caso come questo, senza parentesi mi pare veramente un
ottimo modo per cercare grane :D
 
-- 
Una volta ho chiesto la differenza tra
l'incesto e l'incendio. Non credo di aver
capito troppo bene perché dopo un po' ho
dato fuoco a mia sorella (Pippo Franco)
 

 manuale del compilatore e controllare come l'hanno implementato.
 
Avevi ragione, avevo capito male.
 
Ma mi resta un dubbio. Se l'incremento, quando e' suffisso, viene
effettuato prima dell'operatore di dereference allora come fa
l'istruzione
 
while(*s++ = *t++)
 
a copiare la stringa puntata da t in quella puntata da s? Infatti, il
contenuto di t[0] non dovrebbe essere copiato in s[0], perche'
dovrebbero essere effettuate le operazioni nel seguente ordine:
 
1)viene incrementato t che puntera' a t[1]
2)viene effettuato il dereference di t
3)viene effettuato l'incremento di s che puntera' a s[1]
4)viene scritto nella locazione di memoria di s[1] il valore di t[1]
5)viene valutato il valore di s[1] e se e' diverso da zero il ciclo
continua.
 
Invece il Kernighan e Ritchie afferma nel capitolo 5 che:
il valore di *t++ e' il carattere a cui punta t prima che t venga
incrementato, il suffisso ++ non altera t finche' il carattere non e'
prelevato (cfr. pag 105 versione inglese o 104 versione italiana)
C'e' qualcosa che mi sfugge, ma non vedo cos'e'!

 
 
No. L'operatore di deference è associativo da destra a sinistra.
Ciao!


Ma ha priorita' piu' bassa del suffisso ++, quindi dovrebbe prima
incrementare e poi dereferenziare.

 incrementare e poi dereferenziare.
 
Gli inc/dec postfix sono eseguiti al sequence point successivo (nel caso in 
esame è l'assegnamento). Quindi su "v=*p++" v prende il valore di *p che poi
viene incrementato.
Ciao!


Grazie per la risposta, ma come si stabilisce che viene effettuato al
sequence point successivo?


Non capisco i tuoi dubbi.
 
Prendiamo il ++. Se è prefix il valore viene incrementato prima dell'utilizzo,
se è postfix viene incrementato dopo l'utilizzo. Se vuoi sapere esattamente
"quando" è al sequence point.
 
*p++ da solo significa: prima prendi *p, e quando "hai fatto" fai l'incremento.
 
Per la lista completa dei sequence points basta prendere lo standard (e comunque
sono tra gli argomenti dei bitwise (&&, ||), tra la prima e la seconda parte
dell'operatore ternario (?:), in tutti i controlling (if/while/etc), sui 
return, chiamata a funzione, initializer e ovviamente a fine statement (;) -
sempre che non me ne sia scordato qualcuno).
 
Ciao!
 
P.S. Spero la tua fosse una curiosità e non un dubbio, perché qui stiamo
parlando di "finezze" che nemmeno dovrebbero essere prese in considerazione
nella scrittura di un buon codice (se hai un dubbio te che scrivi, meglio
mettere più parentesi per evitare che anche chi leggerà possa confondersi).
Fanno parte di quelle cose che un programmatore in C *deve* sapere, ma eliminare
i side effects quanto più si può è un bene per tutti!


Ora mi e' chiaro! Avevo le idee piuttosto confuse.
Ma ti spiego da dove venivano i miei dubbi.
Volevo scorrere tutto un vettore di caratteri e volevo farlo con
l'istruzione:
while(*s++);
 
che nella mia mente doveva prendere il valore puntato da s testarlo e
avanzare. Giunto al terminatore \0 fallendo il confronto del while
credevo si fermasse e invece non fa questo perche' arriva allo \0 ma
incrementa comunque s che cosi' non mi indicava la fine dell'array
 
Invece ho visto che questa stessa cosa si fa o con:
while(*++s)
oppure con
while (*s)
    s++;
 
e non mi spiegavo perche' poi scrivendo while(*s++ = *t++) si poteva
ottenere la copia della stringa puntata da t nella stringa puntata da
s senza incappare nello stesso problema di finire fuori dalla stringa
puntata da t.
 
La spiegazione se ho capito bene e' che c'e' un'operazione in piu' da
compiere: l'assegnamento. Quindi, da quanto ho capito, avremo che
l'istruzione *t++ da sola (e dovremmo intendeci su cosa voglia dire da
sola, la sto pensando ad esempio inserita da sola in un while(.)),
esegue prima l'incremento di t e poi estrae il valore, e questo
perche' dovrebbe ubbidire alla priorita' degli operatori; il suffisso
ha infatti priorita' maggiore rispetto all'operatore di indirezione
quindi in una stessa espressione qual è *t++ dovrebbe essere eseguito
prima dell'operatore *. La stessa scrittura *t++, invece, quando e'
inserita in una espressione qual e' da ritenersi l'assegnamento =,
determina un comportamento diverso. Infatti, in tal caso, essendo
l'operatore ++ suffisso, deve essere prima valutata l'espressione in
cui la variabile e' inserita, cioe' verra' effettuato l'assegnamento
di *t a *s (e nel caso del while anche la verifica se e' diversa da 0)
e poi, verra' effettuato l'incremento del puntatore essendo
l'operatore ++ suffisso.
 
Questi meccanismi li ho dedotti empiricamente ma andrebbero visti
diversamente: guardando il codice assembly prodotto dal compilatore
per vedere qual e' la sequenza precisa delle operazioni. Ho capito che
si puo' vedere cosi' ma non lo so leggere e capire e poi ci sono le
possibili ottimizzazioni che fa il compilatore che mi renderebbe
ulteriormente complicato capire come funzionano le cose.

 ulteriormente complicato capire come funzionano le cose.
 
IMHO vedere l'assembly non ti aiuterebbe per nulla, anzi, per me confonderebbe
solo le idee. L'importante è sapere quello che il compilatore fa, non "come"
lo fa: è l'unico modo per riuscire a scrivere codice portabile (anche su
processori con assembly differenti).
 
Provare un po' tutto quello che ti viene in mente, invece, è un ottimo modo
per fissare le idee :)
 
Ciao!


 
non c'entra nulla l'operazione in più dell'assegnamento.
Dovresti vedere la cosa in questo modo:
while(*s++);
 
equivale a:
do {
   p = *s;
   s++;
   } while(p);
 
e nell'assegnamento:
while(*s++ = *t++);
 
equivale a:
do {
   p = *t;
   t++;
   *s = p;
   s++;	
   } while(p);
 
Ciao

while(*s++);
 
sia s puntatore a char
diNuovo:
vede il char a cui punta s e lo ritorna, incrementa s di un char
   while
     se il byte ritornato prima e' zero goto fuori
     [potrebbe essere prima conversione da char a int etc]
     altrimenti goto diNuovo
fuori:
 
tutte il resto sono chiacchiere inutili
 


se posso suggerire un consiglio
procurati di sapre *PRIMA DI TUTTO* quello che fa goto
 


Beh cosa fa goto dovrebbe essere semplice! Dovrebbe saltare,
incondizionatamente, alla riga di codice indicata dall'etichetta. Nel
caso da te prospettato: salta alla label "diNuovo" oppure
all'etichetta "fuori"

    } while(p);
 
sebbene il suffisso ++ abbia priorita' minore dell'operatore di
indirection *. Noto, infatti, che viene eseguito prima l'indirection e
poi il suffisso ++, e non il viceversa come suggerirebbe la tabella
della priorita' degli operatori.
 
Ciao e grazie della spiegazione


Hanno la stessa priorità e l'associazione va da destra a sinistra.
In questo caso la priorità ce l'ha il ++ e puoi vederlo come *(p++)
Se, come dici tu, avesse maggiore priorità l'indirezione, verrebbe 
incrementato il contenuto e non il puntatore e sarebbe (*p)++.
 
L'operatore ++ è un po' "speciale", può essere di pre-incremento o di 
post-incremento.
Nel caso in oggetto è di post-incremento e vuol dire:
salva il contenuto attuale in una variabile temporanea, incrementa la 
variabile, usa il valore precedentemente salvato.
Nel caso di pre-incremento invece è semplicemente:
incrementa la variabile, usa il contenuto attuale.
 
Quando non ci interessa il contenuto, ma vogliano solo 
incrementare(decrementare) il puntatore/variabile sarebbe preferibile 
usare il pre-incremento perché, come visto sopra, c'è un passaggio in meno.
Naturalmente tutti i compilatori attuali sanno ottimizzare e quindi in 
pratica usare l'uno o l'altro diventa indifferente.
 
Andando un po' OT ... in C++ si può usare l'operatore ++ anche su 
oggetti complessi e anche se il compilatore al 99% riesce comunque ad 
ottimizzare, dargli una mano è sempre cosa buona :-) Quindi meglio 
prendere l'abitudine di usare il pre-incremento quando possibile e non 
ci interessa usare il contenuto.
Ciao

 Hanno la stessa priorità
 
Penso potrai facilmente capire che nella mia mente si crea una certa
confusione mentale se uno legge questa tabella:
http://en.wikipedia.org/wiki/Operators_in_C_and_C%2B%2B#Operator_precedence
 
 
E vede che li' c'e' scritto che * (indirection) ha precedenza 3,
mentre ++ (suffix increment) ha precedenza 2. Ossia che ++ ha
precedenza piu' alta di *
 


in quella tabella è stato gestito separatamente il prefisso dal suffisso 
e al suffisso è stata data una priorità maggiore ma con associatività da 
sinistra verso destra. In pratica ... è la stessa cosa
 
Ciao