[C++]Errore memcpy.asm

di il
6 risposte

[C++]Errore memcpy.asm

Salve, stavo debuggando (speravo per l'ultima volta) il mio gioco della briscola quando però mi sono trovato in una situazione molto insiduosa.
mi viene lanciato l'errore:
"Eccezione generata in corrispondenza di 0x00007FFD80E61D67 (vcruntime140d.dll) in Briscola.exe: 0xC0000005: violazione di accesso durante la scrittura del percorso 0x000000000000000F. Se esiste un gestore per questa eccezione, il programma può continuare."
utilizzo visual studio 2015 community per compilare e sono sotto windows 10.
lanciandomi tale finestra mi viene aperto un file in assembly (memcpy.asm) e mi viene evidenziata questa riga:
mov     [rax], ecx              ; write four bytes to destination
il compilatore mi dice che nel codice c++ l'errore ha origine in questa funzione:
void PartitaBriscola::inizializzaMazzo(void)
{
	cout << "Inizio ad inizializzare il mazzo." << endl;

	for (int i = 0; i < mazzo.getCarteAttuali(); i++)
	{
		cout << "Indice = " << i << endl;
		system("pause");

		if (i < 10)
			mazzo[i].setSeme("denari");
		else if (i < 20)
			mazzo[i].setSeme("coppe");
		else if (i < 30)
			mazzo[i].setSeme("spade");
		else
			mazzo[i].setSeme("bastoni");

		cout << "seme assegnato." << endl;
		system("pause");

		mazzo[i].setNumero(i % 10 + 1);
		cout << "numero assegnato." << endl;
		system("pause");

		switch (i % 10)
		{
		case 0:
			mazzo[i].setNome("asso");
			mazzo[i].setPunteggio(11);
			break;
		case 1:
			mazzo[i].setNome("due");
			break;
		case 2:
			mazzo[i].setNome("tre");
			mazzo[i].setPunteggio(10);
			break;
		case 3:
			mazzo[i].setNome("quattro");
			break;
		case 4:
			mazzo[i].setNome("cinque");
			break;
		case 5:
			mazzo[i].setNome("sei");
			break;
		case 6:
			mazzo[i].setNome("sette");
			break;
		case 7:
			mazzo[i].setNome("fante");
			mazzo[i].setPunteggio(2);
			break;
		case 8:
			mazzo[i].setNome("cavallo");
			mazzo[i].setPunteggio(3);
			break;
		case 9:
			mazzo[i].setNome("re");
			mazzo[i].setPunteggio(4);
			break;
		}

		cout << "punteggio e nome assegnati." << endl;
		system("pause");
	}

	cout << "Ho finito di inizializzare il mazzo." << endl;
}
(perdonate gli orrendi system pause ma in fase di debug speravo mi aiutassero...)
l'errore, più precisamente, proviene dal setSeme. ho provato a debuggare il programma varie volte e spesso si ferma quando i è uguale a 3, mentre raramente quando i = 1.
questa è la funzione setSeme della classe carta:
bool Carta::setSeme (const string& seme)
{
	if (seme != "")
	{
		this->seme = seme;
		return true;
	}
	else
		return false;
}
quando il programma crashava lanciando l'eccezione si bloccava sempre mentre assegnava il seme, quindi credo sia quello il problema.
il progetto ha 15 file, se volete debuggarlo personalmente potete scaricare tutto qui (google drive).

vi ringrazio in anticipo per la lettura e spero mi possiate dare una mano dato che io non capisco proprio cosa fare...

6 Risposte

  • Re: [C++]Errore memcpy.asm

    Il vero problema lo crei in questa riga:
    
    PartitaBriscola::PartitaBriscola(const int n)
    {
    	mazzo = Mazzo(40);
    	...
    
    in quanto non hai ne un costruttore di copia, ne un operatore di assegnamento. In pratica l'invocazione di Mazzo(40) crea una variabile temporanea che poi viene assegnata a mazzo, MA questa variabile temporanea subito dopo esce di scope e libera il puntatore carte che è uguale bit per bit a quello di mazzo. Pertanto ogni successiva operazione su mazzo è invalidata.
    Soluzioni:
    - aggiungere costruttore di copia e operatore di assegnamento a ogni classe che usi puntatori raw
    - usare std::vector<> al posto degli "array dinamici" fatti con puntatori raw (consigliato).
  • Re: [C++]Errore memcpy.asm

    shodan ha scritto:


    Il vero problema lo crei in questa riga:
    
    PartitaBriscola::PartitaBriscola(const int n)
    {
    	mazzo = Mazzo(40);
    	...
    
    in quanto non hai ne un costruttore di copia, ne un operatore di assegnamento. In pratica l'invocazione di Mazzo(40) crea una variabile temporanea che poi viene assegnata a mazzo, MA questa variabile temporanea subito dopo esce di scope e libera il puntatore carte che è uguale bit per bit a quello di mazzo. Pertanto ogni successiva operazione su mazzo è invalidata.
    Soluzioni:
    - aggiungere costruttore di copia e operatore di assegnamento a ogni classe che usi puntatori raw
    - usare std::vector<> al posto degli "array dinamici" fatti con puntatori raw (consigliato).
    grazie per la risposta, credevo non arrivasse più!
    ho effettuato numerose correzioni al codice nel frattempo, tuttavia ancora qua e là ci sono errori abbastanza bastardi con la memoria.
    i vector ora come ora devo imparare bene come funzionano e dato che è un progetto scolastico e non siamo ancora arrivati ai vector, preferisco momentaneamente avere una versione funzionante fatta con questi raw pointers.
    ora l'errore s'è spostato e avviene in fase di gioco, la zona d'errore è questa:
    bool TavoloBriscola::confronta (Carta& a, Carta& b)
    {
    	cout << "Confonto le carte." << endl;
    
    	if (a.getSeme() != seme)
    	{
    		cout << "false." << endl;
    		return false;
    	}
    	else if (b.getSeme() != seme)
    	{
    		cout << "true." << endl;
    		return true;
    	}
    	else if (a.getPunteggio() > b.getPunteggio())
    	{
    		cout << "true." << endl;
    		return true;
    	}
    	else if ((a.getPunteggio() == 0 && b.getPunteggio() == 0) && a.getNumero() > b.getNumero())
    	{
    		cout << "true." << endl;
    		return true;
    	}
    	else
    	{
    		cout << "false." << endl;
    		return false;
    	}
    }
    aggiorno il contenuto della cartella su google drive, sperando di finire sto progetto una volta per tutte...
  • Re: [C++]Errore memcpy.asm

    Come prima, l'errore è da un'altra parte e per la precisione in:
    
    int TavoloBriscola::trovaVincitore(const int leader)
    {
    	cout << "Calcolo il seme." << endl;
    	setSeme(leader);
    
    	int winner = leader;
    	Carta max = carte[leader];
    	int indice = leader + 1; // (punto 1)
    
    	cout << "Calcolo la carta piu' alta." << endl;
    	for (int i = 1; i < carteAttuali; i++) // scorre tutte le carte finché non ha trovato la vincitrice di mano
    	{
    		if (indice == carteAttuali) // (punto 2) 
    			indice = 0;
    		else // altrimenti incrementato
    			++indice;
    
    		if (confronta(carte[indice], max) == true) // (punto 3)
    		       ...
           
    
    In pratica, quando leader vale 0 succede che indice vale 1 (punto 1). In (punto 2) la if fallisce il confronto (carteAttuali vale 2) e incrementa indice che ora vale 2.
    Ora stai accedendo a carte[2] che non esiste! Quell'array ha due locazioni non tre, e ne imposti la dimensione nel costruttore di Tavolo.
    (Per la cronaca, per trovare l'errore ho dovuto usare std::vector<Carte> in modo che il debugger mostri locazioni e numero di elementi contenuti. Capisco il progetto scolastico, ma questo dimostra che, quando possibile, è meglio usare i vector rispetto agli array con raw pointer).

    Tra l'altro nel costruttore di tavolo stai usando una strategia di controllo allocazione diversa (e inutile) da quella che usi da altre parti (corretto controllo eccezioni).

    Un controllo simile funziona solo quando si scrive:
    
    	carte = new Carta[n] nothrow; // nothrow forza l'uso di un operator new che non lancia eccezioni, ma ritorna nullptr (NULL)
    
    	if (carte != NULL)
    	{
    
    
    P.S.
    Non capisco il senso dell'allocazione nella funzione swap() contenuta in mazzo.cpp
  • Re: [C++]Errore memcpy.asm

    Grazie mille per l'aiuto che mi hai dato, penso finalmente d'esser riuscito a far funzionare bene sta briscola

    shodan ha scritto:


    Come prima, l'errore è da un'altra parte e per la precisione in:
    
    int TavoloBriscola::trovaVincitore(const int leader)
    {
    	cout << "Calcolo il seme." << endl;
    	setSeme(leader);
    
    	int winner = leader;
    	Carta max = carte[leader];
    	int indice = leader + 1; // (punto 1)
    
    	cout << "Calcolo la carta piu' alta." << endl;
    	for (int i = 1; i < carteAttuali; i++) // scorre tutte le carte finché non ha trovato la vincitrice di mano
    	{
    		if (indice == carteAttuali) // (punto 2) 
    			indice = 0;
    		else // altrimenti incrementato
    			++indice;
    
    		if (confronta(carte[indice], max) == true) // (punto 3)
    		       ...
           
    
    In pratica, quando leader vale 0 succede che indice vale 1 (punto 1). In (punto 2) la if fallisce il confronto (carteAttuali vale 2) e incrementa indice che ora vale 2.
    Ora stai accedendo a carte[2] che non esiste! Quell'array ha due locazioni non tre, e ne imposti la dimensione nel costruttore di Tavolo.
    sì, quella funzione faceva schifo, come setSeme, perché inizialmente avevo impostato il tavolo come un array che non conteneva in modo ordinato le carte, ma ogni indice indicava la carta che ha scartato il corrispettivo giocatore (ma appunto non erano in ordine, nella mia testa il giro era dal giocatore 0-1-2-3 in senso antiorario), e poi in partitaBriscola.cpp con la funzione addCarta non mi ero accorto che ero tornato esattamente indietro: i metodi per trovare il vincitore e il seme si basavano su questo pensiero del "so che carta hai lanciato", però l'array anziché essere riempito per indici veniva riempito in ordine, ecco quindi il casino.

    shodan ha scritto:


    (Per la cronaca, per trovare l'errore ho dovuto usare std::vector<Carte> in modo che il debugger mostri locazioni e numero di elementi contenuti. Capisco il progetto scolastico, ma questo dimostra che, quando possibile, è meglio usare i vector rispetto agli array con raw pointer).
    guarda, ho subito voglia di imparare sti vector e inizio a pocciare qualcosa perché veramente i raw pointer mi hanno stancato abbastanza dopo sti problemi

    shodan ha scritto:


    Tra l'altro nel costruttore di tavolo stai usando una strategia di controllo allocazione diversa (e inutile) da quella che usi da altre parti (corretto controllo eccezioni).

    Un controllo simile funziona solo quando si scrive:
    
    	carte = new Carta[n] nothrow; // nothrow forza l'uso di un operator new che non lancia eccezioni, ma ritorna nullptr (NULL)
    
    	if (carte != NULL)
    	{
    
    
    qui sono stato colto da un dubbio amletico: se l'allocazione non andava a buon fine cosa facevo?
    improvvisamente mi ricordo del c, dove le eccezioni non c'erano e se la malloc (su cui sapevo si basava l'operatore new) non allocava correttamente restituiva un puntatore nullo. così da furbone penso di poter fare sto controllo in c++ anche, poi leggo su cppreference che invece la new lancia la bad_alloc e, ovviamente, mi scordo di modificare tutti i sorgenti

    shodan ha scritto:


    P.S.
    Non capisco il senso dell'allocazione nella funzione swap() contenuta in mazzo.cpp
    intendi allocare dinamicamente temp?
    in effetti non è stato molto furbo (come altre cose del resto), speravo contasse qualcosa a livello di prestazioni ma non è saggio scomodare il sommo heap per una stupida temp.

    ti ringrazio ancora per l'aiuto
  • Re: [C++]Errore memcpy.asm

  • Re: [C++]Errore memcpy.asm

    shodan ha scritto:


    Ci sarebbe da fare un lungo discorso su questo e nello specifico sulla bad_alloc. Se l'allocazione della memoria fallisce, sia con un NULL sia con una bad_alloc, significa che non c'è più trippa per gatti per cui si dovrebbe terminare immediatamente il programma.
    Tuttavia dato che una classe può ridefinire il proprio operatore new per motivi suoi (di solito una strategia di allocazione svicolata dal sistema operativo), una bad_alloc potrebbe non essere così critica da dover imporre un arresto immediato del programma. Però è una situazione considerata C++ avanzato per cui si deve valutare caso per caso. Nel dubbio si termina il programma.
    P.S.
    Oltre ai vector da un'occhiata anche a shared_ptr<> e unique_ptr<>. Ho notato che alcune volte allochi memoria ma non la rilasci.
    immagino, ma credo che data la mia inesperienza in c++ sono problemi che per ora non mi dovrei porre.
    ad ogni modo ho fatto una versione nuova con i vector e funziona tutto correttamente, anche con l'algoritmo, e devo dire che i vector sono millemila volte più comodi dei raw pointer: se andavo in overflow con un raw pointer succedeva il finimondo, mi apriva xstring, memcpy o altri file scritti in arabo (per me), mentre se vado in overflow con un vector dà direttamente l'errore out of range e grazie ai vari cout riesco risalire subito all'errore
    ti ringrazio per il tuo aiuto
Devi accedere o registrarti per scrivere nel forum
6 risposte