Autor Wątek: Przenoszenie wielowymiarowych tablic  (Przeczytany 3338 razy)

Offline _user

  • Użytkownik

# Lipiec 20, 2015, 17:59:51
Ciagle mam problemy z przenoszeniem kilkuwymiarowych tablic do funkcji... wczesniej pamietam jak pisalem to przenosilem w taki sposob chyba, jesli dobrze pamietam:
void foo(int [][x])
Ale nie wiem, teraz potrzebuje przeniesc taki kod do funkcji:
Kod: (c) [Zaznacz]
  for(int i=0;i<l;++i){
    for(int j=0;j<k;++j){
      scanf("%i", &a);
      tab[i][j]=a;
    }
  }
Pobiera liczby do tablicy wedlug ilosci wierszy i kolumn, poradzilby ktos jak przeniesc taka tablice do funkcji ? Oraz zeby tablica zmieniona w funkcji zmieniala sie globalnie.

Offline Mr. Spam

  • Miłośnik przetworów mięsnych

Offline lethern

  • Użytkownik

  • +1
# Lipiec 20, 2015, 18:17:10
Uhm, nieważne jaką tablicę przekazujesz, zawsze jest to pod spodem wskaźnik (adres)
jedyne co musisz uzupełnic dla kompilatora, to typ tego wskaźnika, żeby kompilator mógł Cię zgaić za ewentualne pomyłki
(można też przekać tablicę tak, żeby kompilator nas się nie czepiał, ale wolimy żeby opcja wyplucia błędami była dostępna)
tablica dwuwymiarowa intow to int**
żadnej innej magii nie potrzebujesz, skoro przekazujesz do funkcji adres tablicy, to ta funkcja będzie rzeźbić po tej właśnie wskazanej tablicy (globalnej, jak ją nazwałeś). Więc warto podkreślić, że nie kopiujesz do funkcji tablicy, a kopiujesz do funkcji adres (kopiowanie argumentów nie znika)

Aha, dla wszystkiego podkreślę drugi raz
NIE przenosisz tablicy do funkcji, tablica od momentu zadeklarowania pozostaje w swym miejscu, jedynie udostepniasz różnym kawałkom kodu jej adres, żeby mogły do niej sięgnąć

Przekazanie funkcji argumentu różni się w przypadku wskaźnika a zmiennej z wartością tym, że kopiując dla funkcji wartość, funkcja zmienia kopię wartości, a przekazując (kopiując) funkcji wskaźnik (adres), funkcja ma kopię adresu, ale ten adres wskazuje na ciągle ten sam obiekt. Z tym się komputer rodzi i z tym się nie dyskutuje..
« Ostatnia zmiana: Lipiec 20, 2015, 18:23:16 wysłana przez lethern »

Offline jelcynek

  • Użytkownik

  • +1
# Lipiec 20, 2015, 18:27:11
void foo(int** tab, int width, int height)

Offline _user

  • Użytkownik

# Lipiec 20, 2015, 18:27:42
Cos kompilator sie nie zgadza, dalem int**, co jest zle ?
Kod: (c) [Zaznacz]
void foo(int **tab,int l,int k){
  int a;
  for(int i=0;i<l;++i){
    for(int j=0;j<k;++j){
      scanf("%i", &a);
      tab[i][j]=a;
    }
  }
}

int main(void){
  int l,k;
  scanf("%i %i",&l,&k);
  int tab[l][k];
  foo(tab,l,k);

  return 0;
}

Offline mawpa

  • Użytkownik

  • +1
# Lipiec 20, 2015, 18:32:50
Źle jest np. to, że nie robisz dynamicznej alokacji pamięci.

Offline _user

  • Użytkownik

# Lipiec 20, 2015, 18:38:01
A co mialbym zaalokowac i gdzie ? tablice ? w funkcji foo czy w main ? I jak prawidlowo przeniesc tam ten adres ?

Offline lethern

  • Użytkownik

  • +1
# Lipiec 20, 2015, 18:41:54
int tab[l][k];to nie jest ogólnie poprawna alokacja tablicy i program powinien Ci się tu wywalić z hukiem (poza jednym odłamem C, w którym faktycznie takie coś zaimplementowali, nie pamiętam bo nie piszę w C (mam nadzieję, że za mocno nie przekręcam faktów). Jeśli akurat piszesz w tym czymś... cóż....  a w tym ogólnym przypadku chodzi o to, że rozmiar tablicy musi być znany w trakcie kompilacji, nie w trakcie uruchomienia programu. I moja poprzednia wypowiedź odnosi się do tej "normalnej sytuacji", w której wyżej cytowana linijka jest błędna)
« Ostatnia zmiana: Lipiec 20, 2015, 18:45:11 wysłana przez lethern »

Offline _user

  • Użytkownik

# Lipiec 20, 2015, 18:44:39
Z jakim hukiem... ja zawsze tak deklaruje tablice... Faktycznie moze powinienem czesciej uzywac dynamicznej alokacji ;p Ale error wywala tylko przy tym:
foo(tab,l,k);
i to przede wszystkim chcialbym naprawic
« Ostatnia zmiana: Lipiec 20, 2015, 18:48:08 wysłana przez _user »

Offline lethern

  • Użytkownik

  • +1
# Lipiec 20, 2015, 18:53:11
To co używasz nazywa się variable length array zdefiniowane w C99
Do rozwiązania swego probemu mógłbyś użyć rozszerzenia GNU (opisanego tutaj jako parameter forward declaration), z tym że C99 samo z siebie (które wprowadza VLA) nie wspiera tego ; )
Ja nie pisze w C99, więc obawiam się że bardziej Ci nie pomogę, może ktoś inny wpadnie z rozwiązaniem

Offline _user

  • Użytkownik

# Lipiec 20, 2015, 19:00:22
Okej poprawilem sie troszke, ale w czasie dzialania programu wywala mi naruszenie ochrony pamieci, moze tu bys umial pomoc:
Kod: (c) [Zaznacz]
#include<stdio.h>
#include<stdlib.h>

void foo(int **tab,int l,int k){
  int a;
  for(int i=0;i<l;++i){
    for(int j=0;j<k;++j){
      scanf("%i", &a);
      tab[i][j]=a;
    }
  }
}
void bar(int **tab,int l,int k){
   for(int i=0;i<l;++i){
    for(int j=0;j<k;++j){
      printf("%i",tab[i][j]);
    }
  }
}
 
int main(void){
  int l=0,k=0,r=0;
  int **tab;
  tab=(int**)malloc(r*sizeof(**tab));
  scanf("%i %i",&l,&k);
  foo(tab,l,k);
  bar(tab,l,k);
  free(tab);
  return 0;
}

Offline mawpa

  • Użytkownik

# Lipiec 20, 2015, 19:01:34
int l,k;
int** tab;

scanf("%i %i", &l, &k);

tab = malloc(l * sizeof(int*));
for(int i = 0; i < l; ++i)
  tab[i] = malloc(k * sizeof(int));

foo(tab, l, k);
 
for(int i = 0; i < l; ++i)
  free(tab[i]);
free(tab);

I wszystko działa jak powinno.

Offline _user

  • Użytkownik

# Lipiec 20, 2015, 19:05:26
@mawpa no okej dzieki, nie rozumiem tylko tego zwalniania na koncu, moglby ktos wytlumaczyc ?
for(int i = 0; i < l; ++i)
  free(tab[i]);
to zwalnia tylko wiersze chyba nie ? a gdzie zwalnianie pamieci zaalokowanej na kolumny ? i czemu nie mozna odrazu zrobic tylko
free(tab)
nie wystarcza ?
oraz nie rozumiem jeszcze tutaj
tab = malloc(l * sizeof(int*));
for(int i = 0; i < l; ++i)
  tab[i] = malloc(k * sizeof(int));
czemu w pierwszym mamy sizeof(int*) a w drugim juz sizeof(int) ?
« Ostatnia zmiana: Lipiec 20, 2015, 19:09:47 wysłana przez _user »

Offline lethern

  • Użytkownik

  • +1
# Lipiec 20, 2015, 19:12:01
tab to adres na tablicę, w której znajdują się wskaźniki
każdy z nich to adres na tablice intów
więc jeśli masz l = 10, k = 20
to deklarujesz jedną tablicę wielkości 10 adresów
następnie deklarujesz 10 tablic wielkości 20 intów
razem masz 11 tablic
jeśli zwolnisz jedną tablicę, free(tab), to zostanie Ci 10 niezwolnionych tablic
czyli inaczej: na każdy użyty jeden malloc musisz miec jeden free

Jednocześnie odpowiedziałem Ci na drugie pytanie, ale rozwinę:
pierwsza tablica zawiera adresy (dlatego deklarujesz ją jako sizeof int*, czyli przeciętnie 4B)
druga tablica (w zasadzie ciąg tablic) zawiera inty, dlatego jest to sizeof int, czyli przeciętnie 4B

Powtórzę, przy k,l=  10x10 deklarujesz 11 tablic, z czego jedna z nich przechowuje tylko adresy pozostałych 10
« Ostatnia zmiana: Lipiec 20, 2015, 19:13:48 wysłana przez lethern »

Offline _user

  • Użytkownik

# Lipiec 20, 2015, 19:14:15
Wywala mi errory przy tym kodzie w tych linijkach:
  tab=malloc(l*sizeof(int*));
  for(int i=0;i<l;++i)
    tab[i]=malloc(k*sizeof(int));
takie:
       a.c: In function ‘int main()’:                                                                                         
       a.c:25:13: error: invalid conversion from ‘void*’ to ‘int**’ [-fpermissive]                                             
          tab=malloc(l*sizeof(int*));                                                                                         
                    ^                                                                                                         
       a.c:27:18: error: invalid conversion from ‘void*’ to ‘int*’ [-fpermissive]                                             
            tab[i]=malloc(k*sizeof(int));                                                                                     
                   
« Ostatnia zmiana: Lipiec 20, 2015, 19:21:42 wysłana przez _user »

Offline lethern

  • Użytkownik

  • +1
# Lipiec 20, 2015, 19:24:18
Co do błędu, to co zwraca malloc jest typu void*, a Ty oczekujesz albo int* albo int**, więc musisz dodać rzutowanie (w komunikacie masz napisane, ze oczekuje int*)

Przy okazji
Tutaj masz trochę naowijane w bawełnę
#include <cstdio>
#include <cstdlib>

int main()
{
typedef int T_element;
typedef int* T_tablica_elementow;
typedef int** T_tablica_tablic;

T_tablica_tablic tab_of_tab;

int l=10, k=10;

/// chcemy tablicę 10 wskaźników
tab_of_tab = (T_tablica_tablic)   malloc(l * sizeof(T_tablica_elementow));

for(int i = 0; i < l; ++i){
T_tablica_elementow&   tab_of_elem = tab_of_tab[i];
/// chcemy tablicę 10 intów
tab_of_elem = (T_tablica_elementow)    malloc(k * sizeof(T_element));

for(int j=0; j<k; ++j) tab_of_elem[j]= 0;
}

tab_of_tab[1][1] = 11;
tab_of_tab[7][7] = 77;
T_tablica_elementow    tab_4 = tab_of_tab[4];
tab_4[8] = 48;

T_element&    el_4_2 = tab_4[2];
el_4_2 = 42;


for(int j=0; j<k; ++j) {
for(int i = 0; i < l; ++i)
printf("%d ", tab_of_tab[i][j]);
puts("\n");
}


for(int i = 0; i < l; ++i){
T_tablica_elementow   tab_of_elem = tab_of_tab[i];
free( tab_of_elem );
}

free(tab_of_tab);
}
Co prawda w dwóch miejscach używam referencji &, żeby rozbić coś na pośrednią zmienną, tylko na potrzeby pokazania... u siebie oczywiście tego nie używaj
« Ostatnia zmiana: Lipiec 20, 2015, 19:30:15 wysłana przez lethern »