Iterowanie dwuwymiarowej tablicy wskaźnikami

0

Witam,

męczę się cały dzień z poruszaniem się za pomocą wskaźników po dwuwymiarowej tablicy tworzonej dynamicznie (za pomocą operatora new). Chcę poruszać się po niej w taki sposób, aby iterując wyciągać wpierw dane z kolumny.
Macierz jest kwadratowa, 10x10.

Otóż działa poniższy kod:

        size = 10;
        float** m1; // inicjalizowana wcześniej tablica
        float *mptr;

	for(int j = 0; j < size; ++j)
	{
		for(int i = 0; i < size; ++i)
		{
			mptr = m1[i] + j;
			cout << (long)*mptr << " ";
		}
		cout << endl;
	}

Przykład który nie działa (pierwsza kolumna wyświetlona dobrze, w następnych w niektórych miejscach wyświetlane są złe wartości albo śmieci):

        float* mbase = m1[0]; // adres podstawowy

	for(int j = 0; j < size; ++j)
	{
		for(int i = 0; i < size; ++i)
		{
			mptr = mbase + size * i + j;
                        // próbowałem też: mptr = mbase + size * i * sizeof(float) + j;

			cout << *mptr << " ";
		}
		cout << endl;
	}

Mam jeszcze takie pytanie, czym różni się zapis: m1[0] od &m1[0] ? I czy ten drugi zapis ma sens w ogóle (adres z adresu?) Pytam, bo gdy wipisuję sobie adres za pomocą cout << (long)&m1[0]; albo (long)m1[0] to dostaję dwie różne wartości...

Pozdrawiam,
gals

1

Nie ma czegoś takiego jak dwuwymiarowa tablica. Każda tablica w C/C++ jest w taki czy inny sposób zapisywana jako wektor, czyli tablica o jednym wymiarze. "Dwuwymiarowa" tablica alokowana dynamicznie to tablica wskaźników na poszczególne wiersze/kolumny (zależnie od przyjętej konwencji, dalej będę pisał o kolumnach)
Ilustruje to poniższe dzieło:
2darray.png
Poszczególne kolumny nie muszą leżeć obok siebie w pamięci. Wynika to z faktu, że nie alokujesz jednego dużego bloku a kilka mniejszych. Z tego powodu nie można tak po prostu przeskoczyć z końca jednej kolumny na początek kolejnej (drugi kod, ten który nie działa), po drodze trzeba się dowiedzieć gdzie jest ten początek (pierwszy kod, ten który działa).

Przede wszystkim powinieneś zapomnieć o tych wskaźnikach. Używaj normalnie operatorów [], które się nie pomylą. Całkiem niedawno był wątek, z którego wynika, że z wykorzystania wskaźników nie wynikają żadne widoczne korzyści.

Po drugie do implementacji tablic dwuwymiarowych lepiej jest używać odpowiednio dużej tablicy jednowymiarowej. O tym jak to zrobić możesz poczytać w artykule "Dwuwymiarowe tablica są złe".

Co do tych adresów - spójrz na rysunek. Twoje m1[0] to wartość elementu, czyli adres kolumny. Natomiast &m1[0] to adres tego elementu - w tym wypadku jest to dokładnie to samo co samo m1 bo to początek tablicy.

1

jak już wspominał @Endrju nie ma tak naprawdę tablic wielowymiarowych(dane w pamięci i tak są przechowywane sekwencyjnie). Więc do iterowania po wielowymiarowej tablicy można użyć zwykłego wskaźnika o tak:

 
int tab[20][20] = {0};
int *wsk  = tab[0]; //musieliśmy podać jeden wymiar ,ponieważ kompilator może skonwertować na adres początku tablicy wyrażenie które jest jedno            //wymiarową tablicą( int[]) Gdybyśmy podali samą nazwę tablicy wielowymiarowej i chciali przypisać to do wskaźnika to byłby to błąd, ponieważ w tym //przypadku wyrażenie miało by taką postać int[][].

Później możemy iterować po tej tablicy po prostu tak: wsk++
Za każym razem będziemy tak jakby przeskakiwać o jeden element dalej znajdującym się najbardziej po prawej stronie. Jeśli prekroczyliśmy 20 element to indeks o jeden w lewo tak jakby zwiększa się o jeden. Dalej jest taki sam schemat. Może tak iterować przez 20 * 20 razy = 400

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