Old 04-19-2015, 03:03 PM   #1
MarioNintendo
Expect delays.
FFR Simfile AuthorFFR Music ProducerFFR Veteran
 
MarioNintendo's Avatar
 
Join Date: Mar 2008
Location: Montreal, QC
Age: 26
Posts: 3,866
Default Coding help

To whom may be able to lend me a hand,

I am trying to write a code for my C++ class which basically fixes the language's problem of accessing elements out of bounds for a particular buffer using classes and operator overloading. It sounds like basic stuff once you know what's what, and that's the reason why I don't understand why I'm having trouble with this. Here is my code:

Code:
#include <iostream>
using namespace std;

// création du patron de classe
template <class Type> class monTableau
{
	private: 
		Type *Ptr; //pointeur sur un tableau
		int borneInf;	// borne inférieure des indices du tableau
		int borneSup;	// borne supérieure des indices du tableau
	public:
		monTableau<Type>(int inf, int sup)	// Constructeur de tableau
		{
			Ptr += inf;	// Le pointeur avance (ou recule) jusqu'a la borneInf donnée par l'usager
			borneInf = inf;
			borneSup = sup;

			// Test si les bornes inf et sup sont en ordre croissant
			if (inf > sup)
			{
				cout << "Les indices du tableau doivent suivre un ordre croissant." << endl;
			}

			// Si les bornes suivent un ordre croissant, on remplit le tableau d'éléments vides
			else
			{
				for (int i=inf ; i < sup ; i++)
				{
					*(Ptr+i)=0;    // edit: This line is supposedly a big part of the problem based on testing
				}
			}
		}

		// Surdéfinition de l'opérateur [], permet d'éviter les débordements de tableaux
		Type operator [] (const int n)
		{
			// Si le n demandé ne fait pas partie des indices du tableau
			if (n < borneInf || borneSup < n)
			{
				cout << "Débordement de tableau, ne peut pas accéder à l'élément " << n << "." << endl;
			}

			// Si on n'a pas retourné de message d'erreur, ...
			else 
			{
				return *(Ptr + n);	// ... on retourne l'élément pointé, Ptr + n
			}
		}

		// Surdéfinition de l'opérateur ==, compare l'élément du tableau Ptr avec l'élément visé (elem).
		bool operator == (const Type & elem)
		{
			if (*this == elem)	{return true; cout << "test2" << endl;} 
			else			return false;
		}

		// Surdéfinition de l'opérateur !=, retourne les valeurs opposées de l'opérateur == surdéfini ci-haut.
		bool operator != (const Type & elem)
		{
			if(*this == elem)	return false;
			else			return true;
		}
} ;

int main()
{
	monTableau<int> tableau1(3,5);
	cout << tableau1[4]; 
//	monTableau<int> tableau2(1,2);    // This line makes it seg fault
//	cout << (tableau1[4]==tableau1[4]) << endl;    // This line also makes it seg fault
	return 0;
}
The code was conceived in french, but basically replace "tableau" by "buffer" in your head, and you will be fine. The class monTableau should be able to let me create buffers with custom index numbers (defined by the user in borneInf and borneSup). I won't detail it too much here, but in case someone would feel comfortable helping out, it would be my pleasure to hook up on skype and look at it together.

edit: im adding comments to the code as I find the problematic lines.
__________________
Click here to download my complete works!


Quote:
Originally Posted by macgravel View Post
Mario's files are top notch, so every time he submits a file I get excited in my pants.
Click the bricks



Last edited by MarioNintendo; 04-19-2015 at 03:23 PM..
MarioNintendo is offline   Reply With Quote
Old 04-19-2015, 03:48 PM   #2
MarioNintendo
Expect delays.
FFR Simfile AuthorFFR Music ProducerFFR Veteran
 
MarioNintendo's Avatar
 
Join Date: Mar 2008
Location: Montreal, QC
Age: 26
Posts: 3,866
Default Re: Coding help

So yeah. My issue at the moment is when I want to access what my pointer Ptr is pointing to.

*Ptr, Ptr[0] or any variation of this gets me a seg fault at the moment.

Edit: well, I see that when I make *Ptr as public, the seg faults disappear. I am curious however what should be added to my code for it to work with Ptr set as private.
__________________
Click here to download my complete works!


Quote:
Originally Posted by macgravel View Post
Mario's files are top notch, so every time he submits a file I get excited in my pants.
Click the bricks



Last edited by MarioNintendo; 04-19-2015 at 04:32 PM..
MarioNintendo is offline   Reply With Quote
Old 04-19-2015, 05:24 PM   #3
AutotelicBrown
Under the scarlet moon
FFR Simfile AuthorD7 Elite KeysmasherFFR Veteran
 
AutotelicBrown's Avatar
 
Join Date: Jan 2014
Age: 26
Posts: 850
Default Re: Coding help

Quote:
Type *Ptr; //pointeur sur un tableau
When you create a monTableau<int>, Ptr is a non-initialized pointer of type (int *). It's not pointing to anything so anything you try do with it may lead to a seg-fault (in the best case scenario, otherwise it's undefined behavior).

What you should do is allocate new memory of size (sup - inf + 1) with the operator new and change the operator[] to offset the index by -inf. The boundary check is ok.

Edit: Something like
Code:
    monTableau<Type>(int inf, int sup) :
      Ptr(NULL) , // Can be allocated here but it would be before boundary check
      borneInf(inf) ,
      borneSup(sup)
    {
       [...]
       Ptr = new Type[sup-inf+1]; // Allocate memory for the array
       [...]
    }

    ~monTableau<Type>()
    {
      delete[] Ptr; // Must free allocated memory
    }

    Type& operator [] (const int n)
    {
      [...]
      return Ptr[n-borneInf];
    }

Last edited by AutotelicBrown; 04-19-2015 at 05:55 PM..
AutotelicBrown is offline   Reply With Quote
Old 04-20-2015, 01:23 AM   #4
MarioNintendo
Expect delays.
FFR Simfile AuthorFFR Music ProducerFFR Veteran
 
MarioNintendo's Avatar
 
Join Date: Mar 2008
Location: Montreal, QC
Age: 26
Posts: 3,866
Default Re: Coding help

Thanks so much. Indeed, I had beginner's problem with dynamic memory allocation with my pointers. Here's the code I ended up with, which does what I wanted it to do, if it could help anyone in the future:


Code:
// CODE RÉDIGÉ PAR MARIONINTENDO
// monTableau.cpp, permet de manipuler des tableaux qui empêchent les débordements de tableaux
#include <iostream>
#include <stdlib.h>
using namespace std;



// création du patron de classe
template <class Type> class monTableau
{
	private: 
		Type *Ptr; //pointeur sur un tableau
		int borneInf;	// borne inférieure des indices du tableau
		int borneSup;	// borne supérieure des indices du tableau
		int nelem;	// Le nombre d'éléments dans mon tableau
		monTableau(const monTableau &source);	// On ne veut pas le copy constructor
	public:
		monTableau<Type>(int sup)	// Constructeur de tableau avec 1 argument
		{

			// Test si les bornes inf et sup sont en ordre croissant
			if (0 > sup)
			{
				cout << "Les indices du tableau doivent être positifs s'ils commencent a 0." << endl;
				exit(EXIT_FAILURE);	// On cesse l'exécution du code
			}

			// Si les bornes suivent un ordre croissant, on crée le tableau
			else
			{
				borneInf = 0;	// Par défaut, la borne inférieure sera nulle
				borneSup = sup;	// On prend la borne supérieure spécifiée par l'usager
				Ptr = new Type[borneSup-borneInf+1];	// On alloue une mémoire dynamique de bonne dimension au pointeur
				nelem = borneSup-borneInf;	// Le norme d'éléments est la grandeur de l'intervalle couvert par les bornes
				for (int i=0 ; i < borneSup ; i++)
				{
					*(Ptr+i)=0;	// On initialise les éléments du tableau à 0
				}
			}
		}

		monTableau<Type>(int inf, int sup)	// Constructeur de tableau avec 2 arguments
		{

			// Test si les bornes inf et sup sont en ordre croissant
			if (inf > sup)
			{
				cout << "Les indices du tableau doivent suivre un ordre croissant." << endl;
				exit(EXIT_FAILURE);	// On cesse l'exécution du code
			}

			// Si les bornes suivent un ordre croissant, on crée le tableau
			else
			{
				borneInf = inf;		// Les bornes inférieures et supérieures seront celles définies par l'usager
				borneSup = sup;
				Ptr = new Type[borneSup-borneInf+1];	// On alloue une mémoire dynamique a notre pointeur
				Ptr += inf;	// Le pointeur avance (ou recule) jusqu'a la borneInf donnée par l'usager
				nelem = borneSup-borneInf;	// On compte le nombre d'élément dans l'intervalle couvert par le tableau
				for (int i=borneInf ; i < borneSup ; i++)
				{
					*(Ptr+i)=0;	// On initialise les éléments du tableau à 0
				}
			}
		}
		// Surdéfinition de l'opérateur [], ici le test pour éviter les débordements de tableaux
		Type &operator [] (const int n)
		{
			// Si le n demandé ne fait pas partie des indices du tableau
			if (n < borneInf || borneSup < n)
			{
				cout << "Débordement de tableau, ne peut pas accéder à l'élément " << n << ". (Ce tableau va de " << borneInf << " à " << borneSup << ".)" << endl;
				exit(EXIT_FAILURE);	// On cesse l'exécution du code
			}

			// Si Ptr[n] existe. ...
			else 
			{
				return *(Ptr + n);	// ... on retourne l'élément pointé, Ptr + n
			}
		}

		// Surdéfinition de l'opérateur ==, compare l'élément du tableau Ptr avec l'élément visé (elem).
		bool operator == (const monTableau<Type> &  elem)
		{
			int count=0;	// Compteur qui sera égal a nelem si this == elem. On l'utilise pour le test d'égalité.
			for (int i=borneInf ; i<borneSup ; i++)
			{
				if (*(Ptr+i) == elem.Ptr[elem.borneInf+i-1]) count++;	// Si les éléments de chaque tableau est le même au même endroit (même s'ils n'ont pas nécessairement les mêmes indices), on les considère comme égaux
			}
			if (count == nelem) return true;	// Si tous les éléments sont égaux, les tableaux sont égaux...
			else		    return false;	// ... Sinon, il ne sont pas égaux.
		}

		// Surdéfinition de l'opérateur !=, retourne les valeurs opposées de l'opérateur == surdéfini ci-haut.
		bool operator != (const monTableau<Type> & elem)
		{
			int count=0;	// Compteur qui sera égal a nelem si this == elem. On l'utilise pour le test d'égalité.
			for (int i=borneInf ; i<borneSup ; i++)
			{
				if (*(Ptr+i) == elem.Ptr[elem.borneInf+i-1]) count++;	// Si les éléments de chaque tableau est le même au même endroit (même s'ils n'ont pas nécessairement les mêmes indices), on les considère comme égaux

			}

			if (count == nelem) return false;	// Si tous les éléments sont égaux, les tableaux sont égaux...
			else		    return true;	// ... Sinon, il ne sont pas égaux.
		}	
		
		void operator = (const monTableau<Type> &droite)
		{
			if (this != &droite)	// Si les deux objets observés ne sont pas à la même adresse déjà...
			{
				borneInf = droite.borneInf;	// L'objet à gauche de l'égalité reçoit les bornes de l'objet à droite
				borneSup = droite.borneSup;
				Ptr = new int [nelem = droite.nelem];	// On réalloue un nouvel espace dynamique pour le pointeur de l'objet de gauche
				nelem = droite.nelem;	// L'objet de gauche a autant d'éléments que celui de droite
				for (int i=borneInf ; i < borneSup ; i++)
				{
					Ptr[i] = droite.Ptr[i];	// On rend chacun des éléments du tableau de gauche égal à ceux du tableau de droite
				}
			}	
		}

		Type operator = (const Type &value)
		{
			return value;	// Si l'on assigne une valeur à un élément de tableau, celui-ci devient la valeur qu'on lui a imposée.
		}

		// Fonction qui va afficher le contenu d'un tableau sous la forme {a,b,...,z}
		void affiche()
		{
			cout << "{";
			for (int i=borneInf ; i < borneSup ; i++)
			{
				cout << Ptr[i];
				if (i<borneSup-1) cout << ", ";	// Mettre des virgules après les nombres, mais pas après le dernier
			}
			cout << "}";
			cout << endl;
		}
} ;

int main()
{
	monTableau<int> tab1(1,6);
	cout << "tab1 = ";
	tab1.affiche();
	tab1[1] = 1;
	tab1[2] = 2;
	tab1[3] = 3;
	cout << "tab1 = ";
	tab1.affiche();
	monTableau<int> tab2(10);
	cout << "tab2 = ";
	tab2.affiche();
	cout << "(tab1 == tab2) vaut " << (tab1 == tab2) << endl;
	cout << "(tab1 != tab2) vaut " << (tab1 != tab2) << endl;

	cout << "On fait tab2 = tab1." << endl;
	tab2 = tab1;

	cout << "tab1 = ";
	tab1.affiche();
	cout << "tab2 = ";
	tab2.affiche();

	cout << "(tab1 == tab2) vaut " << (tab1 == tab2) << endl;
	cout << "(tab1 != tab2) vaut " << (tab1 != tab2) << endl;

	monTableau<float> tabFloat(5);
	tabFloat[0] = 1;
	tabFloat[1] = 1166.55;
	tabFloat[4] = 3.6;
	cout << "tabFloat = ";
	tabFloat.affiche();

	monTableau<double> tabDouble(-10,10);
	tabDouble[-10] = 22451./12932;
	tabDouble[9] = 123.42314731;
	cout << "tabDouble = ";
	tabDouble.affiche();
	
	return 0;
}

// RÉSULTATS DE TESTS D'EXÉCUTION:
// -------------- TEST 1: Le main() est identique que celui ci-haut --------------
// tab1 = {0, 0, 0, 0, 0}
// tab1 = {1, 2, 3, 0, 0}
// tab2 = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
// (tab1 == tab2) vaut 0
// (tab1 != tab2) vaut 1
// On fait tab2 = tab1.
// tab1 = {1, 2, 3, 0, 0}
// tab2 = {1, 2, 3, 0, 0}
// (tab1 == tab2) vaut 1
// (tab1 != tab2) vaut 0
// tabFloat = {1, 1166.55, 0, 0, 3.6}
// tabDouble = {1.73608, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 123.423}
//
//
// -------------- TEST 2: On modifie le main() pour vérifier les erreurs possibles ----------------
// int main()
// {
//	monTableau<int> tabFail_1(-4);
// 	return 0;
// }
//
// ON OBTIENT COMME RÉSULTAT D'EXÉCUTION
//Les indices du tableau doivent être positifs s'ils commencent a 0.
//
//---------------------------------------------------------------------------------------------
//int main()
//{
//	monTableau<int> tabFail_2(5,2);
//	return 0;
//}
//
// ON OBTIENT COMME RÉSULTAT D'EXÉCUTION
// Les indices du tableau doivent suivre un ordre croissant.
//
//-------------------------------------------------------------------------------------------------
//int main()
//{
//	monTableau<int> tabFail_3(2,5);
//	cout << tabFail_3[1];	
//	return 0;
//}
//
//ON OBTIENT COMME RÉSULAT D'EXÉCUTION
//Débordement de tableau, ne peut pas accéder à l'élément 1. (Ce tableau va de 2 à 5.)


One thing I intentionally omitted is the use of delete in the destructor ~monTableau. It made the pointer to freak out since, as far as I can tell, it was trying to access an outdated destination via a modified pointer. Welp. One day, I'll learn to fix that particular mistake. In the meantime, I'm off to bed.
__________________
Click here to download my complete works!


Quote:
Originally Posted by macgravel View Post
Mario's files are top notch, so every time he submits a file I get excited in my pants.
Click the bricks



Last edited by MarioNintendo; 04-20-2015 at 01:24 AM..
MarioNintendo is offline   Reply With Quote
Old 04-20-2015, 03:32 AM   #5
AutotelicBrown
Under the scarlet moon
FFR Simfile AuthorD7 Elite KeysmasherFFR Veteran
 
AutotelicBrown's Avatar
 
Join Date: Jan 2014
Age: 26
Posts: 850
Default Re: Coding help

You have to call delete[] on the original value you get for Ptr when you call new[], but you are changing it in the following line:

Code:
  Ptr += inf;	// Le pointeur avance (ou recule) jusqu'a la borneInf donnée par l'usager
Not sure how exactly you are trying to work doing that but it would lead to some unnecessary things to work correctly. As it currently is you are actually writing out of bounds.


I'll call the original Ptr you get from new Ptr_0 and your modified value Ptr_1 which is Ptr_0 + inf. As you allocated sup - inf + 1, that I'll call Size, elements in dynamic memory at Ptr_0, it means its valid values are from *(Ptr_0) to *(Ptr_0 + Size - 1).

As Ptr_1 - inf == Ptr_0, you can substitute Ptr_0 in that range to get *(Ptr_1 - inf) to *(Ptr_1 -inf + Size - 1) == *(Ptr_1 + sup - 2*inf) as your valid range for Ptr_1. Note how on your current code you actually go from *(Ptr_1 + inf) to *(Ptr_1 + sup - 1) which doesn't really make sense and go out of bounds:

Code:
	for (int i=borneInf ; i < borneSup ; i++)
	{
		*(Ptr+i)=0;	// On initialise les éléments du tableau à 0
	}

With Ptr_1 you'd have to do the following to achieve the correct operation:

Code:
	for (int i = 0; i < borneSup - borneInf + 1 ; i++)
	{
		*(Ptr - borneInf + i)=0;
	}
Additionally operator [] would be:
Code:
	return *(Ptr - (2*borneInf) + n);

But what I suggest is not using Ptr_1 in the first place and using the original Ptr_0 so you get:

Code:
	for (int i = 0; i < borneSup - borneInf + 1; ++i)
	{
		*(Ptr + i) = 0;
	}
And for the operator [] you'd have:
Code:
	return *(Ptr + n - borneInf);


You'd have to correct all the uses of Ptr in a similar way. Note that what you had only worked out of luck for getting valid addresses.


Additionally, nelems should be borneSup-borneInf+1. There is also some best practices when implementing operator= especially if you need to allocate memory on the heap because new can throw an exception, but I guess that's beyond your current needs.
AutotelicBrown is offline   Reply With Quote
Reply


Currently Active Users Viewing This Thread: 1 (0 members and 1 guests)
 
Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are Off
[IMG] code is On
HTML code is Off

Forum Jump



All times are GMT -5. The time now is 07:35 AM.


Powered by vBulletin® Version 3.8.1
Copyright ©2000 - 2020, Jelsoft Enterprises Ltd.
Copyright FlashFlashRevolution