Newsgroup: it.comp.lang.c++
Titolo: Anomalia del compilatore?

Salve,
Sicuramente conoscerete quegli indovinelli in cui ci sono dei recipienti con
determinate capacità di contenimento e tramite versamenti da un recipiente
all'altro e a svuotamenti, bisogna riuscire a far rimanere un certo
quantitativo di liquido all'interno di un recipiente.
Esempio: si hanno 2 recipienti, uno con una capacità di 5 litri e l'altro di
3 litri. Considerando che non ci sono limiti di acqua e che quindi ogni
recipiente può essere riempito e svuotato quante volte si vuole, in che modo
si possono far rimanere 4 litri nel recipiente da 5?
Ho scritto  un programma per risolvere rompicapo di questo tipo, ma mi
succedono delle cose strane.
Uso Windows 7 a 64 bit.
Ho notato che in alcuni casi non funziona, ovvero a parità di input
inseriti, a volte funziona altre volte va in crash.
L'ho compilato con l'ultima e la penultima versione di MinGW (porting di gpp
su Windows) e ho lo stesso problema.
Ma la cosa ancora più strana è che usando un altro compilatore (un vecchio
bcc32 del 2002) ottengo anomalie diverse da quelle ottenute con MinGW.
Posso capire che il bcc32 sia troppo vecchio e poco attendibile, ma
possibile che dia problemi anche il MinGW? E tra l'altro si verificano solo
in alcuni casi.
L'assurdità raggiunge il culmine in certi casi in cui dato un certo input,
il programma funziona.
Provo a passare altri input e crasha.
Riprovo i precedenti input quelli per cui funzionava ma crasha di nuovo.
Riprovo nuovamente e funziona.
Il tutto provato anche sul PC di un mio amico che ha installato il sistema
operativo (anche lui Windows 7 a 64 bit) una settimana fa, quindi non credo
sia un problema di sistema operativo corrotto.
Possibile che ci sia un bug nel compilatore?
O magari da problemi su sistemi a 64 bit?
Per chi volesse provarlo con un altro compilatore, ecco una versione del
programma in cui non c'è richiesta di input, i dati sono già inseriti, e
l'output deve essere la soluzione dell'esercizio postato precedentemente,
ovvero:
*** 6 mosse ***
Riempio 1       5(5) 3(0) inf(inf)
Verso 1 in 2    5(2) 3(3) inf(inf)
Svuoto 2        5(2) 3(0) inf(inf)
Verso 1 in 2    5(0) 3(2) inf(inf)
Riempio 1       5(5) 3(2) inf(inf)
Verso 1 in 2    5(4) 3(3) inf(inf)
Dovete solo compilarlo ed eseguirlo. Possibilmente fatelo più volte, in modo
da valutare se il comportamento cambia o meno:
#include <iostream>
#include <string>
#include <vector>
#include <sstream>
using namespace std;
vector<vector<int> > Backup;
vector<pair<int, int> > Versamenti;
vector<string> Mosse;
enum {Soluzione = -2, Infinito = -1};
string Int2Str (int i) {
   ostringstream o;
   o << i;
   return o.str();
}
bool Maggiore(vector<int> a, vector<int> b) {
   int i = 0;
   while (i < a.size() && a[i] == b[i])
       i++;
   return i < a.size() && a[i] > b[i];
}
vector<int> DispoR_AdiacDiversi (int n, int k, bool init = false, int pos =
0) {
   static vector<int> v;
   static vector<int> c;
   static vector<int> Mag;
   if (init) {
       v.clear();
       c.clear();
       Mag.clear();
       for (int i = 0; i < n; i++)
           v.push_back(i);
       for (int i = 0; i < k; i++)
           c.push_back(i % 2);
       Mag.resize(k);
       Mag[0] = -1;
       c[c.size() - 1]--;
   };
   int i = k - 1;
   while ((i >= 0 && c[i] == v[v.size() - 1]) || (i > pos && pos > 0))
       i--;
   c[i]++;
   while (i > 0 && c[i] == c[i - 1]) {
       while (i >= 0 && c[i] == v[v.size() - 1])
           i--;
       c[i]++;
   };
   for (int j = i + 1, h = 0; j < k; j++, h++)
       c[j] = v[h % 2];
   if (Maggiore(c, Mag))
       Mag = c;
   else
       c.resize(0);
   return c;
}
class Recipiente {
   private :
       int cap, cont;
   public :
       Recipiente (int capacita, int contenuto = 0) {
           if (contenuto > capacita)
               contenuto = capacita;
           if (contenuto == Infinito)
               capacita = Infinito;
           cap = capacita;
           cont = contenuto;
       }
       void operator= (Recipiente& c) {
           if (cap != Infinito && c.cap != Infinito) {
               int vuoto = cap - cont;
               cont += c.cont;
               c.cont -= vuoto;
               if (cont > cap)
                   cont = cap;
               if (c.cont < 0)
                   c.cont = 0;
           }
           else
           if (cap != Infinito && c.cap == Infinito) {
               int vuoto = cap - cont;
               cont = cap;
               if (c.cont != Infinito) {
                   c.cont -= vuoto;
                   if (c.cont < 0)
                       c.cont = 0;
               };
           }
           else
           if (cap == Infinito && c.cap != Infinito) {
               if (cont != Infinito)
                   cont += c.cont;
               c.cont = 0;
           };
       }
       int Cap() {
           return cap;
       }
       int Cont() {
           return cont;
       }
       void Svuota() {
           cont = 0;
       }
       string Info() {
           string s = "";
           if (cap == Infinito)
               s = "inf";
           else
               s = Int2Str(cap);
           s += "(";
           if (cont == Infinito)
               s += "inf";
           else
               s += Int2Str(cont);
           s += ")";
           return s;
       }
};
void Salva (vector<Recipiente> r, vector<vector<int> >& v) {
   vector<int> n_upla;
   for (int i = 0; i < r.size(); i++)
       n_upla.push_back(r[i].Cont());
   v.push_back(n_upla);
}
bool Esiste (vector<int> v, vector<vector<int> > b) {
   int con = 0;
   for (int i = 0; i < b.size() - 1; i++) {
       for (int j = 0; j < v.size(); j++) {
           if (v[j] == b[i][j])
               con++;
           if (con == v.size())
               return true;
       };
       con = 0;
   };
   return false;
}
bool Risolto (vector<Recipiente> r, vector<int> vf) {
   bool risolto = true;
   for (int i = 0; i < vf.size(); i++)
       risolto = risolto && r[i].Cont() == vf[i];
   return risolto;
}
int Elabora (vector<Recipiente> r, vector<int> vf, vector<int> v) {
   for (int i = 0; i < v.size(); i++) {
       string stato = "";
       if (v[i] < r.size() * (r.size() - 1)) {
           if ((r[Versamenti[v[i]].first].Cont() <
r[Versamenti[v[i]].first].Cap() ||
               r[Versamenti[v[i]].first].Cap() == Infinito &&
r[Versamenti[v[i]].first].Cont() >= 0) &&
               r[Versamenti[v[i]].second].Cont() != 0) {
               int cont_second = r[Versamenti[v[i]].second].Cont();
               r[Versamenti[v[i]].first] = r[Versamenti[v[i]].second];
               for (int j = 0; j < r.size(); j++)
                   stato += r[j].Info() + " ";
               if (cont_second > 0)
                   Mosse.push_back("Verso " +
Int2Str(Versamenti[v[i]].second + 1) + " in " +
Int2Str(Versamenti[v[i]].first + 1) + "\t" + stato);
               else
                   Mosse.push_back("Riempio " +
Int2Str(Versamenti[v[i]].first + 1) + "\t" + stato);
           };
       }
       else {
           if (r[r.size() * r.size() - v[i] - 1].Cont() != 0) {
               r[r.size() * r.size() - v[i] - 1].Svuota();
               for (int j = 0; j < r.size(); j++)
                   stato += r[j].Info() + " ";
               Mosse.push_back("Svuoto " + Int2Str(r.size() * r.size() -
v[i]) + "\t" + stato);
           };
       };
       vector<int> contenuti;
       for (int j = 0; j < r.size(); j++)
           contenuti.push_back(r[j].Cont());
       if (Backup.size() > 0)
           if (Esiste(contenuti, Backup))
               return i;
       Salva(r, Backup);
   };
   if (Risolto(r, vf))
       return Soluzione;
   return 0;
}
int main() {
   int Recip, MinMosse, MaxMosse, capinput, continput, sol;
   bool esci = false;
   vector<Recipiente> R;
   vector<int> VF;
   Recip = 3;
   R.push_back(Recipiente(5, 0));
   R.push_back(Recipiente(3, 0));
   R.push_back(Recipiente(-1, -1));
   Recip = 1;
   VF.push_back(4);
   MinMosse = 1;
   MaxMosse = 6;
   sol = 1;
   cout << "\nStato iniziale capacita'(contenuto)\n" << endl;
   for (int j = 0; j < R.size(); j++)
       cout << (j + 1) << "^ recipiente: " << R[j].Info() << endl;
   cout << endl;
   cout << "Soluzione da trovare: ";
   for (int j = 0; j < VF.size(); j++) {
       if (R[j].Cap() == Infinito)
           cout << "inf";
       else
           cout << R[j].Cap();
       cout << "(" << VF[j] << ") ";
   };
   cout << "\n\n" << endl;
   for (int i = 0; i < R.size(); i++)
       for (int j = 0; j < R.size(); j++)
           if (i != j)
               Versamenti.push_back(pair<int, int>(i, j));
   for (int k = MinMosse; k <= MaxMosse && !esci; k++) {
       cout << "Ricerca della soluzione con " << k << " moss" << (k > 1 ?
"e" : "a") << "\n" << endl;
       vector<int> v;
       bool init = true;
       int pos = 0;
       do {
           v = DispoR_AdiacDiversi(R.size() * R.size(), k, init, pos);
           init = false;
           Backup.clear();
           Mosse.clear();
           if (v.size() > 0)
               if ((pos = Elabora(R, VF, v)) == Soluzione && Mosse.size() ==
k) {
                   cout << "*** " << k << " mosse ***" << endl;
                   for (int i = 0; i < Mosse.size(); i++)
                       cout << Mosse[i] << endl;
                   cout << endl;
                   esci = true;
                   if (sol == 1)
                       break;
               };
       }
       while (v.size() > 0);
   };
   cout << "Elaborazione terminata" << endl;
   while (cin.get() != '\n');
   return 0;
}
Saluti 

 O magari da problemi su sistemi a 64 bit?
Non ho provato il sorgente ma quando si hanno problemi come questi al 99% la 
colpa è di un errore nel programma.
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
 

 O magari da problemi su sistemi a 64 bit?
Scusa se sono schietto, ma che sia il tuo codice ad avere qualche errore 
non lo prendi in considerazione?
Il fatto che a parità di input si comporti in modo diverso a seconda 
dell'esecuzione è facilmente sintomo di *undefined behaviour* (UB).
In particolare, nel tuo codice, l'UB è dovuto all'accesso fuori dai 
limiti di un vettore:
Lanciandolo con un debugger durante l'esecuzione si nota il tentativo di 
accedere all'elemento con indice -1 (che diventa 4294967295, essendo 
l'indice unsigned), da cui l'UB.
D'altra parte l'accesso agli elementi dei vari vettori che utilizzi è 
decisamente complicato da comprendere (e che facilmente porta ad 
errori), ed i nomi che usi (v, c, r, ecc...) non aiutano certo a capire 
il funzionamento dell'algoritmo.
Per quello che ho capito mi pare che l'uso di una std::map potrebbe 
sostituire, semplificando molto, qualcuno dei vettori che usi.
Anche l'overloading dell'operatore = con la modifica del suo "normale" 
comportamento (ossia quello di non modificare lo stato dell'oggetto a 
destra dell'espressione) è una facile fonte di errori.
Ciao.
-- 
http://www.econet-project.eu

 destra dell'espressione) è una facile fonte di errori.
Mi è stato fatto notare, ma ho addirittura provato a togliere quel metodo 
comprese le righe di codice che ne facevano uso, e il crash si è verificato 
lo stesso.
Il problema quindi probabilmente è legato all'indice del vettore di cui 
sopra, ma quale sia il vettore sapresti dirmelo? 

   };
Speriamo che sia questo il problema.
Grazie 

 Windows) e ho lo stesso problema.
Non l'ho provato perché anche se il programma è quel pelo
al di sopra
delle mie capacità di programmatore tanto che potrei
capirlo, dovrei studiarlo un bel po'. Però parlavo di
questi argomenti con un amico che programma a un livello
molto alto e per me è un po' un guru (e ora siamo
temporaneamente colleghi di lavoro) e mi diceva che ormai
GCC è abbastanza vicino alla perfezione. Viene usato così
tanto, così spesso ed è così fondamentale (se non erro ci
si compila Linux) che per trovarne bachi occorre salire
a un livello di sofisticazione veramente elevato.
L'unico bug in cui mi sia per certissimo imbattuto lo
trovai nel Borland, ma non ricordo quale fosse. Peccato
perché è stata una soddisfazione. Ma a parte quello, ogni
volta che qualcosa non mi va so che è il mio programma, e
puntualmente scopro, anche se con tanto lavoro, dove avevo
cannato io.
ciao
Apx.
-- 
Vorrei morire dormendo come mio nonno
e non urlando come i suoi passeggeri.

 cannato io.
Considera che un progetto simile è portato avanti da un team di esperti, per 
cui la probabilità che sia tu (tu generico, quindi anche io :D) a sbagliare 
è sicuramente molto più alta. 

 verificato lo stesso.
Sarebbe bene che modificassi comunque quella parte di codice, 
indipendentemente dal fatto che sia quella la causa degli errori, perché 
č un uso anomalo dell'operatore di assegnamento. Potresti usare gli 
operatori >> e <<;
Ciao.
-- 
http://www.econet-project.eu

 operatori >> e <<;
Umh, l'operatore << forse rende meglio l'idea.
Saluti 

 a un livello di sofisticazione veramente elevato.
Alla faccia del guru......
http://gcc.gnu.org/
Scegli la tua versione preferita e clicca su regression... 248 sul 4.5.1 
282 sulla 4.6.0 (che pero' e' ancora in fase di bug fixing).
Non e' un giudizio di merito (ci mancherebbe, lo uso dalla versione 2 e 
se non ne pensassi bene non lo userei) ma la tua affermazione e' un po' 
ingenua... un pezzo di software cosi' complicato ha "per forza" :-) dei 
bachi.
Max

 dei bachi.
Ma io non ho detto che non ce li ha. Dico che per quanto e
come è usato, probabilmente una percentuale molto ma molto
grande sono stati individuati e fixati, e prima di far
venire fuori un baco con un compilatore come quello penso
occorra fare veramente qualcosa di molto "strano". Poi
posso sempre sbagliare.
ciao!


#include <iostream>
E meno male che il prog e' "relativamente" contenuto. 
Se invece si componeva di qualche migliaia di righe che fa lo postavi
tutto ?