Problem z dynamiczną dwuwymiarową tablicą (C++)

0

Cześć.
Kompilator sygnalizuje błąd w dostępie do pamięci (access violation). Oznacza to, że robię coś źle, a nie bardzo potrafię odnaleźć przyczynę (brak wiedzy).

Jak widać w kodzie poniższym, przy każdym wywołaniu metody dodaj tworzę nową tablice dwuwymiarową o 1 większą niż rozmiar obecnej (na podstawie puli ilosc), po czym kopiuje adresy (wskaźniki) do wcześniej utworzonych tablic dynamicznych. Na koniec usuwam starą tablice i przypisuje jej adres nowej. Przy 3 wywołaniu występuje ów error :S. Proszę szanownych kolegów o wyjaśnienie :)

Klasa:

class RCharLista {
	public:
		int ilosc;
		char ** obiekt;

		RCharLista () {
			obiekt = NULL;
			ilosc = 0;
		}
		~RCharLista () {
			for (int a = 0; a < ilosc; a++)
				delete [] obiekt[a];
			delete obiekt;
		}

		void dodaj (char * rzecz, int rozmiar)
		{
			int a, nowaIlosc = ilosc + 1;
			char ** nowy = new char *[nowaIlosc];

			for (a = 0; a < ilosc; a++) {
				nowy[a] = this->obiekt[a];
				cout << "skopiowano wkaznik nr [" << a << "]: " << nowy[a] << endl;
			}

			if (!ilosc)
				nowaIlosc = ilosc;

			nowy[nowaIlosc] = new char [rozmiar];

			for (a = 0; a < rozmiar; a++)
				nowy[nowaIlosc][a] = rzecz[a];

			cout << "nowy zapis: " << nowy[nowaIlosc] << endl;

			delete this->obiekt;
			this->obiekt = nowy;
			ilosc++;
		}
}

Wycinek z main:

	RCharLista lol;

	char buf1 [] = "umiesz tak?";
	char buf2 [] = "Nie, nie umiem?";
	char buf3 [] = "A to juz by bylo zupelnie niemozliwe?";


	lol.dodaj(buf1, sizeof(buf1));
	lol.dodaj(buf2, sizeof(buf2));
	lol.dodaj(buf3, sizeof(buf3));
1
nowy[nowaIlosc] = new char [rozmiar];

Pierwsza z 3 linijek dajacych AV, reszte sobie domaluj.

0
nowy[nowaIlosc] = new char [rozmiar];

Nie bardzo rozumiem dlaczego tworzenie nowej tablicy w tym miejscu powoduje "AC"?

if (!ilosc)
     nowaIlosc = ilosc;

Sprawdzenie to zapewnia, że gdy licznik jest pusty za pierwszym rozruchem metody (wartość ustalona przez konstruktor), zamiast tworzyć nową tablice w elemencie nowy[1], tworzy do nowy[0]. Przy każdym następnym wywołaniu jest to o 1 dalszy element , tzn nowy[1], nowy[2] itd. zgodnie z wielkością tablicy nowy:

int a, nowaIlosc = ilosc + 1;
char ** nowy = new char *[nowaIlosc];
1
#include <cstring>
#include <iostream>
using namespace std;

class RCharLista
  {
   private:
   unsigned ilosc;
   char **obiekt;
   public:
   operator unsigned()const { return ilosc; }
   const char *operator[](unsigned p)const { return obiekt[p]; }
   RCharLista():ilosc(0),obiekt(0) {}
   ~RCharLista()
     {
      for(unsigned i=0;i<ilosc;++i) delete[] obiekt[i];
      delete[] obiekt;
     }
   void dodaj(const char *rzecz)
     {
      char **nowy=new char *[ilosc+1];
      memcpy(nowy,obiekt,ilosc*sizeof(char*));
      unsigned rozmiar=strlen(rzecz)+1;
      nowy[ilosc]=new char[rozmiar];
      memcpy(nowy[ilosc],rzecz,rozmiar);
      delete[] obiekt;
      obiekt=nowy;
      ++ilosc;
     }
   RCharLista &operator<<(const char *rzecz) { dodaj(rzecz); return *this; }
  };
inline ostream &operator<<(ostream &s,const RCharLista &R) { for(unsigned i=0;i<R;++i) s<<R[i]<<endl; return s; }

int main()
  {
   char buf1[]="umiesz tak?";
   char buf2[]="... niemozliwe?";
 
   RCharLista lol;
   
   lol.dodaj(buf1);
   lol.dodaj("Nie, nie umiem?");
   lol<<"A to juz by bylo zupelnie ..."<<buf2;

   cout<<"LOL:"<<endl<<lol<<"---"<<endl;
   return 0;
  }

http://ideone.com/CEXzs7

0

_13th_Dragon - dziękuję za tak zacny kod. Musiałem trochę się dedukować aby w pełni go zrozumieć :).
Tobie n0name_l też gdyż niejako miałeś racje.
Problemem w moim kodzie był licznik nowaIlosc. Błędnie trzymałem się jego wartości przy tworzeniu char ** nowy = new char *[nowaIlosc]; i choć myślałem, że problem niweluje sprawdzenie czy pula licznik jest pusta, to bardziej go zagmatwał :). W efekcie za pierwszym rozruchem było OK, ale za każdym następnym już nie :), gdyż tworzyłem tablice lub odwoływałem się do niej, choć nie istniał dla niej wskaźnik wewnątrz nowy[x] :). I tak za pierwszym razem odwoływałem się do indeksu 0, 2, 3, 4 itd choć elementów odpowiednio było 1, 2, 3, 4, to też powinienem był się odwoływać do indeksu 0,1,2,3 :).

Niżej zamieszczam poprawiony przeze mnie kod:

void dodaj (char * rzecz)
{
  int rozmiar = strlen(rzecz), a;
  char ** nowy = new char *[ilosc + 1];

  for (a = 0; a < ilosc; a++)
	nowy[a] = this->obiekt[a];

  nowy[ilosc] = new char [rozmiar];

  for (a = 0; a < rozmiar; a++)
	nowy[ilosc][a] = rzecz[a];

  delete this->obiekt;
  this->obiekt = nowy;
  ilosc++;
}

Jeszcze raz Wam dziękuję :). Temat chyba do zamknięcia :).

0

Odświeżam :).
Zgodnie z poradami, zastosowałem się do zaleceń kolegi @_13th_Dragon i nastukałem na klawiaturze następujący kod:

void usun (const int element)
{
   char ** nowy = new char *[ilosc - 1];
 
    if (element)
        memcpy(nowy, obiekt, element * sizeof(char *));
    if (element < ilosc - 1)
        memcpy(nowy[element], obiekt[element + 1], (element - ilosc - 1) * sizeof(char *));
 
   /*        int a;
  for (a = 0; a < element; a++)
  nowy[a] = obiekt[a];
 
  int b;
  for (a = element, b = element + 1; b < ilosc; a++, b++)
  nowy[a] = obiekt[b];
*/
 
   delete obiekt[element];
   delete obiekt;
   obiekt = nowy;
    --ilosc;                        
} 

Czy kod jest właściwy? Pytam aby mieć pewność, że dobrze rozumiem memcpy. Jeżeli coś jest źle proszę o wytłumaczenie :).

1

void * memcpy ( void * destination, const void * source, size_t num ); * http:*www.cplusplus.com/reference/cstring/memcpy/
adresy przekazujesz tylko że nie tablicy zaś elementu tablicy
rozmiar wyliczasz ujemny !!

memcpy(nowy+element,obiekt+element+1,(ilosc-element-1)*sizeof(char*));
memcpy(&nowy[element],&obiekt[element+1],(ilosc-element-1)*sizeof(char*));

1 użytkowników online, w tym zalogowanych: 0, gości: 1