Tablice dynamiczne dwuwymiarowe, wskaźniki

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>


void losowanie1(int tab1[][], int j, int k)
{
    int i, h;
    for(i=0;i<j;i++)
    {
        for(h=0;i<k;h++)
            {
                tab1[i][h]=rand();
            }
    }
}


void wypisz1(int tab1[][], int j, int k)
{
    int i, h;
    for(i=0;i<j;i++)
    {
        for(h=0;i<k;h++)
        {
            printf("%d\t", tab1[i][h]);
        }
        printf("\n");
    }
}
void losowanie2(int tab2[][], int m, int n)
{
    int i, h;
    for(i=0;i<m;i++)
    {
        for(h=0;i<n;h++)
            {
                tab2[i][h]=rand();
            }
    }
}


void wypisz2(int tab2[][], int m, int n)
{
    int i, h;
    for(i=0;i<m;i++)
    {
        for(h=0;i<n;h++)
        {
            printf("%d\t", tab2[i][h]);
        }
        printf("\n");
    }
}




int main()
{
    srand(time(NULL));
    int j, k, m, n;
    printf ("Podaj liczbe wierszy tabeli 1: \t\n");
    scanf ("%d", &j);
    printf ("Podaj liczbe kolumn tabeli 1: \t\n");
    scanf ("%d", &k);
    printf ("Podaj liczbe wierszy tabeli 2: \t\n");
    scanf ("%d", &m);
    printf ("Podaj liczbe kolumn tabeli 2: \t\n");
    scanf ("%d", &n);
    getchar();


    int* tab1=(int*)malloc(sizeof(int)*j*k);
    int* tab2=(int*)malloc(sizeof(int)*m*n);


    losowanie1(tab1[][], j, k);
    wypisz1(tab1[][], j, k);
    losowanie2(tab2[][], j, k);
    wypisz2(tab2[][], j, k);


    free(tab1);
    free(tab2);


    return 0;
}

Mam problem z pewnym programem, a w zasadzie już z samym jego początkiem. Mój problem polega na tym, że poniższy program, który póki co tylko wypełnia tablice dwuwymiarowe o zadanej liczbie wierszy  i kolumn nie kompiluje się. Pojawia się log z błędami:

 

|6|error: array type has incomplete element type|
|18|error: array type has incomplete element type|
|30|error: array type has incomplete element type|
|42|error: array type has incomplete element type|
||In function 'main':|
|70|warning: initialization from incompatible pointer type [enabled by default]|
|71|warning: initialization from incompatible pointer type [enabled by default]|
|73|error: expected expression before ']' token|
|73|error: type of formal parameter 1 is incomplete|
|74|error: expected expression before ']' token|
|74|error: type of formal parameter 1 is incomplete|

|75|error: expected expression before ']' token|
|75|error: type of formal parameter 1 is incomplete|
|76|error: expected expression before ']' token|
|76|error: type of formal parameter 1 is incomplete|
||=== Build failed: 12 error(s), 2 warning(s) (0 minute(s), 0 second(s)) ===|

Mój problem, jak sądzę, polega na jeszcze dość kiepskiej znajomości wskaźników. Najbardziej zależy mi na rozwiązaniu błędów z nimi związanych. 

Program ten pisałem bazując na podobnym, z tym, że pracującym na tablicy jednowymiarowej. Jak się okazuje, przerobienie go na taki, aby w pełni sprawnie działał na tablicach dwuwymiarowych okazało się ponad moje się.

Przejrzałem trochę forów, lecz nigdzie nie mogę znaleźć tego, co mnie interesuje: mianowicie sytuacji, kiedy wielkość tablicy jest zadana przez użytkownika. Wszędzie, gdzie szukałem, napotykałem na przykłady tablic o znanym rozmiarze, a że jestem osobą uczącą się przede wszystkim na przykładach, mam z rozwiązaniem mojego problemu problem.

 

Z góry dziękuję za wszelką pomoc, nakierowanie na dobre rozwiązanie, a także liczę na objaśnienia w przykładach.

 

///Kod jest w trakcie powstawania, mogą być w nim liczne niedociągnięcia niezwiązane z problemem natury wskaźników. Jednakże także ich zauważenie docenię.

 

Nie możesz korzystać z tablic “dwuwymiarowych” bez podania rozmiaru wszystkich bez jednego(lecz nie wiem którego) wymiaru. Jest to spowodowane tym, że tablica zawiera swoje elementy wewnątrz siebie. Kompilator po prostu nie wie, gdzie się znajduje drugi i kolejne elementy tab1, bo nie zna ich rozmiarów. Zamiast tablic wielowymiarowych możesz użyć wskaźników.

Źle deklarujesz tablicę dla definicji swoich funkcji, na razie masz zadeklarowaną tablicę wstaźników do int. W C++ powinieneś używać new a nie malloc i deklaracja powinna wyglądać tak:

int** arr = new int*[sizeY]; for(int i = 0; i < sizeY; ++i) arr[i] = new int[sizeX];

Analogicznie w c korzystając z malloc:

arr = (int**) malloc(sizeX*sizeof(int*)); for (int i = 0; i < sizeX; i++) arr[i] = (int*) malloc(sizeY*sizeof(int));

//Deklarujesz drugą tablicę, skoro przekazujesz wskaźnik to jest to tablica nigdzie nie używana.

Faktycznie, spotkałem się z deklaracjami w stylu:

int tab[][3];

jednakże mój program ma sam pobierać dane o ilości wierszy i kolumn, czyli, o ile dobrze sądzę, tabela nie może mieć przypisanej stałej ilości wierszy/kolumn.

 

Mówisz wskaźniki… ale jak? Proszę o konkrety.

drobok, program piszę w C, więc ten język mnie interesuje. Odnośnie używania dwóch tablic: w skrócie, program ma polegać na tym, że wczytuje dwie tablice o zadanych rozmiarach (tablice nazwijmy A i B), następnie sprawdza, czy można je wymnożyć i jeśli tak, to przydziela pamięć dla tablicy C, w której zapisuje wynik. (za słowo tablica można domyślnie wstawić słowo macierz). Rozumiem jednakże, że to, co w chwili obecnej rozbiłem na dwie funkcje, można zawrzeć w jednej.  :)

Jak wspomniał jeden przedmówca, to zamiast wskaźników możesz skorzystać z tablicy jednowymiarowej i obliczać samemu indeks elementu. Trochę odejdę teraz od tematu. C pozwala na podanie w deklaracji funkcji dla parametru będącego tablicą to, która zmienna odpowiada za jaki wymiar. Nie rozwiąże to jednak twojego problemu - jest to tylko informacja dla programisty.

To, co zrobiłeś w dwóch funkcjach można robić w jednej. Funkcje/procedury wymyślono m.in po to, by ograniczyć ilość kodu wynikowego. Ty skorzystałeś z funkcji, ale ilość kodu wynikowego masz taką sama. Najpierw naucz się tego, by wspólny kod uwspólniać za pomocą funkcji/procedur. Nie koniecznie, ale może to ułatwić wprowadzenie zmian do programu, jak wydzielenie mechanizmów z html do części odpowiadających za prezentację(CSS), treść(XML), i zachowanie(Ecma script). Jest to podobne zjawisko.

Oto rozwiązanie, które powinno działać:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>


void losowanie1(int (*tab1_)[], int j, int k)
{
    int (*tab1)[k] = tab1_;
    int i, h;
    for(i=0;i<j;i++)
    {
        for(h=0;h<k;h++)
            {
                tab1[i][h]=rand();
            }
    }
}


void wypisz1(int (*tab1_)[], int j, int k)
{
    int (*tab1)[k] = tab1_;
    int i, h;
    for(i=0;i<j;i++)
    {
        for(h=0;h<k;h++)
        {
            printf("%d\t", tab1[i][h]);
        }
        printf("\n");
    }
}


int main()
{
    srand(time(NULL));
    int j, k, m, n;
    printf ("Podaj liczbe wierszy tabeli 1: \t\n");
    scanf ("%d", &j);
    printf ("Podaj liczbe kolumn tabeli 1: \t\n");
    scanf ("%d", &k);
    printf ("Podaj liczbe wierszy tabeli 2: \t\n");
    scanf ("%d", &m);
    printf ("Podaj liczbe kolumn tabeli 2: \t\n");
    scanf ("%d", &n);
    getchar();


    int (*tab1)[]=malloc(sizeof(int)*j*k);
    int (*tab2)[]=malloc(sizeof(int)*m*n);


    losowanie1(tab1, j, k);
    wypisz1(tab1, j, k);


    free(tab1);
    free(tab2);


    return 0;
}

Najważniejsze jest tutaj zastosowanie podobnej sztuczki, co dla wskaźników na funkcję, czyli (w tym przypadku):

int (*tab1)[k]

Gdyby nie nawiasy, to miałbyś tablicę wskaźników, a tak mamy wskaźnik na tablicę. Ponieważ zmienna typu tablicowego jest stałym wskaźnikiem, to nie mogłem zastosować tab1[j][k], bo wtedy nie mógłbym przypisać adresu do tablicy, który został przekazany jako prametr. Ponieważ wskaźnik można traktować, jak tablicę, to nie wpływa to na resztę programu. Powinno działać, ale ty to sprawdź.

Poniżej poprawiony program

 

#include <stdio.h>
#include <stdlib.h>
#include <time.h>


void losowanie(int ** tab, int j, int k)
{
int i, h;

	for(i=0 ; i<j ; ++i)
	{
		for(h=0 ; h<k ; ++h)
			tab[i][h]=rand();
	}
}


void wypisz(int ** tab, int j, int k)
{
int i, h;

	for(i=0 ; i<j ; ++i)
	{
		for(h=0 ; h<k ; ++h)
			printf("%d\t", tab[i][h]);

		printf("\n");
	}
}


void alloc_memory(int *** ptab, int j, int k)
{
int i;

	*ptab = (int**)malloc(sizeof(int*) * j);

	if( !(*ptab) )
	{
		printf("bum\n");
		exit(2);
	}

	for(i=0 ; i<j ; ++i)
	{
		(*ptab)[i] = (int*)malloc(sizeof(int) * k);

		if( !(*ptab)[i] )
		{
			printf("bum\n");
			exit(2);
		}
	}
}


void free_memory(int ** tab, int j)
{
int i;

	for(i=0 ; i<j ; ++i)
		free(tab[i]);

	free(tab);
}




int main()
{
int i, j, k, m, n;
int **tab1,** tab2;

	srand(time(0));

	printf ("Podaj liczbe wierszy tabeli 1: \t\n");
	scanf ("%d", &j);
	printf ("Podaj liczbe kolumn tabeli 1: \t\n");
	scanf ("%d", &k);
	printf ("Podaj liczbe wierszy tabeli 2: \t\n");
	scanf ("%d", &m);
	printf ("Podaj liczbe kolumn tabeli 2: \t\n");
	scanf ("%d", &n);
	getchar();

	if( j<1 || k<1 || m<1 || n<1 )
	{
		printf("bum\n");
		return 1;
	}

	alloc_memory(&tab1, j, k);
	alloc_memory(&tab2, m, n);

	losowanie(tab1, j, k);
	wypisz(tab1, j, k);
	losowanie(tab2, m, n);
	wypisz(tab2, m, n);

	free_memory(tab1, j);
	free_memory(tab2, m);

return 0;
}