Newsgroup: it.comp.lang.c
Titolo: Esercizio su precedenza operatori

Ciao,
 
mi sembra ci siano un errore su questo esercizio:
http://i64.tinypic.com/25qf12x.jpg
 
 
Al punto 2 avrei scritto:
a= b+= (c++) -d + --e / (-f)
 
"decremento prefisso" e "-unario"  hanno la stessa precenza ma vanno 
valutati da destra verso sinistra, giusto?
Poi magari il risultato è uguale, ma a livello concettuale, di regole, 
vedo un errore.
 
Grazie
 
---
Questa e-mail è stata controllata per individuare virus con Avast antivirus.
https://www.avast.com/antivirus
 
 

 
 
Sì, è esattamente uguale. Per quel che vale, anch'io ragiono come scritto
sulla jpeg.
 
Ciao!

 a= b+= (c++) -d + --e / (-f)
 
Il punto critico e' c++. c viene prima usato, e poi incrementato.
Quindi le parentesi non vanno messe attorno a c++, ma e' come scrivere:
a= b+= c - d + (--e) / (-f);
c++;
 
Ciao Jack

 sulla jpeg.
 
allora mi sa che non ho capito come funziona.
 
Se ho:
 
int a,b=2,c=3;
a= --b / -c;
 
"--" e "-" hanno la stessa piorità e vanno valutati da destra verso 
sinistra, quindi si esegue prima -c:
c=2
 
poi --b:
b=1
 
e infine b/c ("/" ha priorità inferiore rispetto a "--" e "-")
a=2/1=1
 
Cosa non ho capito? :)


valutati da destra verso sinistra, giusto?
 
Sbagliato. L'ordine di valutazione degli operatori in somme e
sottrazioni e' da sx verso dx, che e' differente da "prima gli unari
ovunque siano in una somma o sottrazione poi gli altri" che invece
vorresti fare tu.
 
Saro' ancora piu' dettagliato della tua immagine jpg, per farti vedere
la differenza, scomponendo l'albero delle precedenze e segnando
l'ordine di valutazione (1)..(n):
 
 
a = b+= c++ - d + --e / -f
a = ( b+= c++ - d + --e / -f )
a = ( b+= ( c++ - d + --e / -f ) )
a = ( b+= ( (c++) - d + --e / -f ) )
a = ( b+= ( (c++) - d + ( --e / -f ) ) )
a = ( b+= ( (c++) - d + ( ( --e ) / -f ) ) )
a = ( b+= ( (c++) - d + ( ( --e ) / ( -f ) ) ) )
            -----  ---    -------   ------
             (1)   (2)      (4)       (5)
             ---------      -------------
                (3)              (6)
                --------------------
                         (7)
     -------------------------------------------
                        (8)
------------------------------------------------
                      (9)
 
 
Se sopra vedi solo scarabocchi, prova a usare un font monospaced.
 
Per il "c++" le parentesi vanno bene, perche' il valore di "c++" e'
sempre "c", mentre l'incremento e' un "side-effect" che ha effetto al
successivo uso di "c". 
 
Semanticamente, si e' vero che il risultato finale e' lo stesso, MA se
il contesto sono operazioni valide. In caso di eccezioni (div. per
zero, overflow, altro) la valutazione si "ferma" all'eccezione senza
valutare il resto, per esempio se "(c++) - d" causasse un
under/overflow, seguendo l'ordine di valutazione che ho descritto, la
variable "e" rimarrebbe intoccata, e invece cambierebbe se "--e"
venisse valutato prima (di "(c++) - d"), cosa che invece non deve
accadere. 
 
Sono piccole disquisizioni, ma alla base di tanti "exploit", mi fa
piacere che ogni tanto se ne parla ;)
-- 
andrea - ri mi cci, name

 piacere che ogni tanto se ne parla ;)
 
Interessante, ma io ho rinunciato a ricordarmele: vado di parentesi. 8-D
 


 
Devi ricordarti che entra in azione un altro concetto importante..
 
Matematicamente, se esegui prima --b o -c è la stessa identica cosa.
 
Tu mi potresti dire, sì, ma è teoricamente importante se scrivo qualcosa come
a = --b / -b
Al che ti risponderei: quella sopra è UB (undefined behavior). Lo è perché
modifichi e usi allo stesso tempo una variabile senza un sequence point in
mezzo.
 
Quindi, per quanto ti riguarda come programmatore c, se è matematicamente
equivalente e tutti i side effects sono separati da sequence points non te
ne devi preoccupare (anche perché dopo il parser, l'ottimizzatore e il
compilatore chissà l'espressione cosa diventa a livello macchina! :) ).
 
Ciao!

 successivo uso di "c". 
 
eh no.
se metti le parentesi il compilatore di calcola prima c++, e poi fa la sottrazione.
Scrivere (c++) e' equivalente a scrivere ++c.
 
Ciao Jack


Qui ha la precedenza degli operatori  e la loro associazione
 
http://it.cppreference.com/w/cpp/language/operator_precedence
 
 
 
E' C++ ma vale anche per il C
 

>Scrivere (c++) e' equivalente a scrivere ++c.
 
Le parentesi "forzano" un'ordine di precedenza, ma non modificano
affatto la semantica degli operatori.
 
Prova questo:
 
int main (void){
  int a=10;
  float b;
  b=(a++)/2.;
  printf("%f",b);
  return 0;


   b=(a++)/2.;
 
qui quello che si vuole fare (con le parentesi, le metti apposta) e': prendi a, aggiungi 1 e poi dividi per 2.
Quello che viene fatto e' prendi a, dividi per 2 e poi alla fine aggiungi 1.
 
Visto che il risultato e' 5 le parentesi in questo caso non vengono cagate.
Quindi o e' un grave errore, oppure (e a questo punto propendo per questa possibilita') non ho capito quando viene applicato il post incremento.
 
perche':
y = x++ * 2;
y = ++x * 2;
 
e' chiaro.
 
ma:
y = (x++) * 2;
mi aspetto si comporti come y = ++x * 2 visto che ho messo una parentesi apposta per far calcolare x++ _prima_ di fare la moltiplicazione.
 
Boh
 
Ciao Jack


 
 
Non e' cosi,le parentesi forzano l'ordine di precedenza come e' stato 
scritto.
 
L'espressione (x++) * 2 funziona cosi:
 
Viene valutato (x++)  che risulta x
 
poi  il risultato (che e' x) viene moltiplicato  * 2
 
viene posto il risultato in y e incrementato x
 
 
 
 
 

 viene posto il risultato in y e incrementato x
 
ok, ci sono arrivato.
in pratica e' come avere (x++ + 0) * 2, che quindi diventa (x + 0) * 2 => x * 2 (con x uguale al valore iniziale).
 
Bello contorto. Continuero' ad evitare di mettere i pre/post incrementi nelle equazioni :P
 
Ciao Jack

>non ho capito quando viene applicato il post incremento.
 
Viene applicato subito ma la sua semantica e' il valore precedente
l'incremento.
 
Per dirla in termini meno informatici ma spero piu' capibili in
generale, a++ significa: il valore di "a" in quel momento e in
quell'espressione, ma se l'ordine di valutazione dell'espressione
trova "a" da qualche altra parte DOPO, allora il valore di "a" e'
quello incrementato.
 
Esercizi:
 
int a=0;
(a-- == a)  e' sempre vero o sempre falso?
(--a == a++) ?
(a++ == a--) ?
(a == a--) ?
 
;)



 
Non e' difficile,basta fare un po di pratica.
Pero' espressioni complesse e contorte e' meglio spezzarle ed usare le 
parentesi,almeno per la leggibilita di chi deve manutenere il codice in 
futuro; e puo essere anche lo stesso individuo fisico a mesi o anni di 
distanza.
Per di piu i compilatori di oggi sono molto capaci ad ottimizzare il 
codice tanto che differenze tra i due modi di scrivere non ce ne sono (o 
sono irrilevanti) a livello di codice macchina generato ed ottimizzato.
 

Guardate, la questione della precedenza degli operatori č
 sicuramente fondamentale.  L'esempio preso in esame mi sembra piů
 da IOCCC che altro, una roba cosě non la userei mai in un
 programma reale, spero cosě anche voi.
-- 
 
 
----Android NewsGroup Reader----
http://usenet.sinaapp.com/
 
 
--- news://
freenews.netfront.net/
 - complaints: 
ne...@netfront.net
 ---

 accadere.
 
Non ricordo precisamente cosa dica lo standard in proposito, ma non 
credo che l'ordine di valutazione dell'espressione sia necessariamente 
quello che hai descritto con le parentesi. Se l'ordine di valutazione di 
--e non influenza il risultato, il compilatore e` libero di piazzare 
quell'esecuzione in qualunque ordine. In sostanza, non e` affatto detto 
che --e sia eseguito prima o dopo di c++. Viene, pero`, sicuramente 
eseguito prima della divisione per f.
 
-- 
4ndre4
"The use of COBOL cripples the mind; its teaching should, therefore, be 
regarded as a criminal offense." (E. Dijkstra)
"Ora, questo "Delta11" non è nulla di più di uno scemo del villaggio" - 
(L'Accalappiafasci)

 in pratica e' come avere (x++ + 0) * 2, che quindi diventa (x + 0) * 2 => x * 2 (con x uguale al valore iniziale).
 
Non esattamente. Quel che bisogna tenere a mente e` che l'incremento 
viene demandato al prossimo sequence point e le parentesi non servono ad 
anticipare quell'operazione.
 
-- 
4ndre4

 eseguito prima della divisione per f.
 
Esatto. 
Lo standard elenca esplicitamente i casi di sequence point
(IIRC: operatore '&&', operatore ',', chiamata di funzione, ';')
e dice che tutto il resto e' valutabile nell'ordine che il
compilatore preferisce (a patto di mantenere la correttezza).
 
In quella espressione c'e' un solo sequence point quindi non c'e'
alcuna garanzia sulla sequenza di valutazione.
 
ciao
Giacomo
 

eseguito prima della divisione per f.
 
Ciao, si è esatto, lo standard lascia il compilatore libero di fare
quello che vuole nell'ordine di valutazione delle espressioni sulle
quali un operatore viene applicato.
 
Nell'esempio che ho fatto, poi liberamente permutare 1 con 2, 4 con 5
e 3 con 6 (come ordine di valutazione).
 
E' il motivo per cui ho dovuto dire che stavo seguendo un ordine
preciso di esecuzione, prima di dire cosa può o non può succedere.
 
Per questo motivo, se usi espressioni con "side-effects" (es. classico
il ++), è consigliabile evitare di usare la stessa variabile coinvolta
in più parti dell'espressione.
 
Per completezza, una volta si poteva dire che l'ordine effettivamente
seguito dipende dal compilatore, ma adesso non più, perché con
l'esecuzione su più thread di una espressione complessa, potrebbe
accadere che una espressione venga valutata prima o dopo un'altra,
senza poterlo predire, in momenti diversi e con lo stesso compilatore.


 seguito dipende dal compilatore, ma adesso non più
 
I thread non c'entrano nulla. Se piu` thread eseguissero pezzi di un'espressione, dovrebbero comunque rispettare un determinato ordine di precedenza. Non c'entra niente come viene eseguito effettivamente il codice. C'entra come il codice viene *tradotto*.

 effettivamente il codice. C'entra come il codice viene *tradotto*.
 
Si tratta solo di efficienza. In a + b, non avendo l'obbligo di fare
prima a, posso calcolare a e b contemporaneamente e fare la somma
appena pronti i due risultati, chi è arrivato prima è irrilevante.
 
Il rispetto di cui parli è comunque corretto in alcuni contesti, per
esempio, l'or logico "a || b || .." del C++ NON valuta 'b' se 'a' è
TRUE, e questo costringe il C++ a valutare prima 'a' e dopo
(eventualmente) 'b' e così via.
 
Dal punto di vista di un programma 'serializzato', questo è efficiente
perché la valutazione in C++ si ferma non appena il risultato è noto,
e il C invece calcola sempre e  comunque tutto, mentre dal punto di
vista 'parallelo' è un handicap perché il C++ è costretto a
serializzare tutte le espressioni una ad una, da sinistra a destra,
mentre il C invece le può valutare tutte in parallelo, non avendo
l'obbligo di fermarsi a risultato finale noto.


 Si tratta solo di efficienza. 
 
Non c'entra niente ne` l'efficienza ne` l'elaborazione parallela, con quanto si sta dicendo in questo thread. Tu stai tirando in mezzo i thread, che non c'entrano nulla. Si sta parlando dell'ordine con cui il compilatore traduce gli step di calcolo dell'espressione. In C, esiste il concetto di "sequence point".