Newsgroup: it.comp.lang.c
Titolo: xor swap in una sola istruzione

Ciao a tutti,
queste tre istruzioni:
  a[0] ^= a[1];
  a[1] ^= a[0];
  a[0] ^= a[1];
scambiano i primi due elementi del vettore di interi a senza usare variabili 
di appoggio. Si dovrebbe poter scrivere anche in una sola riga in questo 
modo:
  a[0] ^= a[1] ^= a[0] ^= a[1];
Mi ha molto stupito vedere che compilando con gcc 4.2 quest'istruzione non 
produce il risultato corretto. Compilando con gcc 3.4 invece effettua 
l'operazione di scambio e risulta equivalente alla versione con tre 
istruzioni. Guardando il codice assembly generato da gcc 4.2 mi sono accorto 
che il problema sta nel fatto che, come prima cosa, vengono caricati in tre 
registri differenti i primi operandi delle tre operazioni di xor che devono 
essere effettuate. Il primo operando della terza operazione, a[0], viene 
però modificato dalla prima operazione e quindi non può essere caricato 
anticipo.
Perchè si comporta così la nuova versione di gcc?
In quali casi possono sorgere problemi simili?
Grazie! Ciao!
Benjamin

 
Ciao,
non potendo provare con quel compilatore avrei solo una domanda:
te lo fa anche con l'ottimizzatore disabilitato?
Un saluto.
dMaxime.

 te lo fa anche con l'ottimizzatore disabilitato?
Con l'opzione "-O0", che è quella di default, gcc non dovrebbe effettuare 
alcuna ottimizzazione se non mi sfugge qualcosa...
Anche compilando con il comando "gcc -O0 xorswap.c" il problema si presenta.
Ciao!
Benjamin


a[0] ^= a[1], a[1] ^= a[0], a[0] ^= a[1];
Anche se non capisco perchè sia necessario scriverlo in una riga
sola...

 ha scritto nel messaggio 
news:1383f7dd-79fc-431a...@33g2000yqm.googlegroups.com...

Volevo dire istruzione non riga... Comunque è vero: non è necessario 
scrivere queste tre operazioni in una sola istruzione. Quello che vorrei 
sapere è perchè quell'istruzione non viene tradotta in modo corretto. In C 
capita molto spesso di scrivere istruzioni composte. Mi piacerebbe capire 
meglio quando si corrono rischi di questo tipo dato che, dal punto di vista 
del linguaggio, non credo proprio che quell'istruzione sia scorretta o 
quantomeno sconsigliata. Sembra più un "errore" della nuova versione di gcc 
tant'è che con la vecchia versione non succede e neanche se al posto di 
elementi di un vettore usi semplicemente due variabili intere.
Ciao!
Benjamin

 Benjamin
In realtà è l'opposto. Senza andare a spulciare lo standard, in
generale quando inserisci un una sola istruzione, o anche in un
passaggio di variabili, l'ordine di esecuzione di espressioni che
hanno "side-effects" è implentation-dependent: ogni compilatore fa un
po' come vuole.
Faccio un altro esempio:
int x = 2; printf("%d %d", ++x, ++x);
Cosa stamperà? :D

Cosa stamperà? :D
Non si sa cosa stamperà! :P
Ho riletto il paragrafo 2.12 di "Linguaggio C" che parla proprio di
questo e c'è scritto che:
- l'ordine di valutazione degli operandi è definito solo per gli
operatori '&&' '||' '?:'e ',';
- l'ordine di valutazione degli argomenti di una funzione non è
definito;
- lo standard impone che gli effetti collatarali conseguenza della
valutazione degli argomenti di una funzione devono essere risolti prima di
chiamare la funzione.
Da un'altra parte c'è scritto che gli effetti collaterali devono essere
risolti nell'ambito dell'esecuzione dell'istruzione, cioè tutti gli effetti
collaterali (aggiornamenti delle variabili) devono essere risolti prima che
venga iniziata l'esecuzione dell'istruzione successiva.
Da questo si deduce che, come hai detto tu, istruzioni composte da
diverse espressioni che presentano effetti collaterali possono avere
risultati indefiniti e quindi è rischioso usarle. Meglio scrivere istruzioni 
separate.
Non mi torna però una cosa. Anche la riga da te scritta:
a[0] ^= a[1], a[1] ^= a[0], a[0] ^= a[1];
è un'istruzione sola. Per il discorso appena fatto non dovrebbe essere
garantito che a[0] nella seconda espressione a[0] ^= a[1] abbia il valore
aggiornato. Però su "Linguaggio C" ho trovato un'istruzione analoga da
questo punto di vista: "c = s[i], s[i] = s[j], s[j] = c;" quindi direi che è
proprio vero che queste istruzioni producono sempre il risultato corretto.
Perchè allora ciò non vale anche per:
a[0] ^= a[1] ^= a[0] ^= a[1];
E' vero che l'ordine di valutazione degli operandi di ^= non è definito ma
la sua associatività sì ed è da destra a sinistra. Quindi la scrittura è
equivalente a:
(a[0] ^= (a[1] ^= (a[0] ^= a[1])));
L'ordine di valutazione degli operandi è indifferente... Il problema sta
semplicemente nel fatto che non aggiorna il valore di a[0] durante
l'esecuzione dell'istruzione ma soltanto alla fine.
Perchè con la tua istruzione non può accadere?
Cos'è che mi sfugge? :P
Ciao!
Benjamin

 aggiornato. 
Invece sì, perché l'operatore "," è uno di quelli che ti garantisce 
l'ordine di esecuzione.
E.

 ha scritto nel messaggio 
news:OIjVk.33333$8%.21998@tornado.fastwebnet.it...

Allora non viene garantito soltanto l'ordine di valutazione ma con 
quest'ordine vengono anche aggiornate le variabili. Mi era sfuggito questo.
Grazie, ciao!
Benjamin