Newsgroup: it.comp.lang.c++
Titolo: test per il programma

Questo simpatico programma (scritto da uno studente) stampa due volte
<bip>arola.
Spiegare perche' e proporre una modifica per far funzionare correttamente il
tutto.
#include <stdio.h>
// questo programmammino tenta e ci riesce speriamo a cambiare il punto di
//ritorno della seconda funzione con quella della prima solo che non abbiamo
capito perché ci
// viene visualizzato due volte la nostra stringa di controllo!!
void copia(int a)
{
int *p = &a;//p punta alla locazione 4 sullo stack
p--; //p sende nella posizione 0 cioč a rtn
ret(*p);//chiamo ret con il valore di rtn!
printf("Non stampa un <bip>");// questa non viene fatta
}
void ret(int rtn)
{
int *p =&rtn;// p sta al valore 4 dello stack
p--;//p sende al valore di ritorno della funzione
*p = rtn;//cambia il punto di ritorno!
}
int main()
{
copia(20);
printf("<bip>arola\n");
}

 tutto.
[...cut...]
Prima di tutto specifichiamo che questo argomento è OT in questo newsgroup,
per il semplice fatto che il comportamento del programma proposto è
variabile a seconda dell'architettura del sistema. Questo newsgroup si
occupa di codice C++ standard, indipendente dalla piattaforma su cui viene
compilato. Inoltre questo è un problema che riguarda più da vicino la
programmazione assembly che quella C++.
Detto questo, passiamo alla risposta.
La funzione ret() esegue l'operazione richiesta, cioè modifica il proprio
indirizzo di ritorno rendendolo uguale a quello della funzione copia(). Il
risultato è che all'uscita di ret() non si ritornerà all'esecuzione di
copia(), bensì ci si comporterà come se si fosse usciti da quest'ultima,
cioè si tornerà in main() e si continuerà con l'esecuzione del programma.
In questo modo però, viene bypassato il processo di stack-unwinding di
copia(), ottenendo una corruzione dello stack (viene lasciata nello stack la
coppia [indirizzo ritorno/argomento] di copia()).
In particolare, quando main() termina, estrae dallo stack l'indirizzo di
ritorno a cui saltare - che di norma vuol dire uscire dal programma - e
trova invece quello che aveva in origine la funzione copia(). Tale indirizzo
punta all'istruzione che segue la chiamata di copia(), cioè printf().
Ecco perché la stringa viene stampata due volte.
Marco

 tutto.
Potresti scrivere tutto il codice? Non sono un esperto ma cosě non capisco
il senso di quello che vuoi fare...
CIAO

 il senso di quello che vuoi fare...
Il codice c'era tutto. Salvo che, strettamente parlando, dava adito ad
undefined behaviour. La spiegazione di cosa succede NEL MIGLIORE DEI
CASI l'ha postata Marco. Ma va notato che basta cambiare compilatore o
cambiare i parametri di ottimizzazione per far si che le cose vadano in
tutt'altro modo!
--
Andrea Ferro
---------
Brainbench C++ Master. Scored higher than 97% of previous takers
Scores: Overall 4.46, Conceptual 5.0, Problem-Solving 5.0
More info 
http://www.brainbench.com/transcript.jsp?pid=2522556

 
Da come e' posto, sembrerebbe il testo di un esercizio.
Mentre sul perche' (in qualche caso particolare, su certi S.O e con certi
compilatori) stampi due volte "<bip>arola" sono state gia' date spiegazioni 
abbastanza esaurienti, non saprei pero' come rispondere al secondo punto: 
"far funzionare correttamente il tutto".
L'obiettivo che sembra porsi questo programma e' 
"fare casino con lo stack" e, devo dire, ci riesce abbastanza bene.
Non e' infatti realistico pensare che questo comportamento sia 
"indesiderato" o comunque causato da una svista o un errore.
Ergo il programma funziona gia' "correttamente" :-)
(Il fatto che stampi due volte "<bip>arola" o piuttosto vada in crash,
e' soltanto un effetto secondario dell' "undefined-behaviour" che sembra
comunque essere lo scopo principale del programma in questione)
Fabio

 "far funzionare correttamente il tutto".
Forse l'obiettivo era quello (vano) di "corrompere lo stack in modo
prevedibile". Ok, lo so che ho detto una stupidaggine, ma se ragioniamo come
chi ha postato il problema, cioč tenendo presente una precisa architettura
(non potrebbe essere altrimenti) e un preciso compilatore, allora:
- lo scopo del programma č quello di modificare l'indirizzo di ritorno di
una funzione (per quale motivo, a noi non interessa);
- il problema č che lo stack non viene pulito dopo questa operazione, dando
luogo ad un comportamento anomalo.
La soluzione (estremamente non portabile, ma ormai mi sembra superfluo
dirlo) sarebbe eliminare dallo stack una quantitŕ di byte pari a
sizeof(int)+sizeof(void*), cioč, nel sistema per cui *sembra* essere
progettato questo programma (intel 32bit), una quantitŕ di 8 byte. Se il
nostro compilatore supporta la direttiva __asm o simile, si potrebbe
scrivere:
__asm pop eax
__asm pop eax
prima di chiudere la funzione main().
Marco