Podwajanie wartości tablicy dwuwymiarowej za pomocą wskaźnika w funkcji w języku C.

0

Witam wszystkich, to mój pierwszy problem ucząc się programowania, jak widać zaczynam od języka C i jedno z moich zadań to zrozumienie tego małego i banalnego programu z książki:

#include <stdio.h>

void podw(int tab[], int rozmiar);

int main(void)

{
   static int smieci[3][4] = {
                         {2,4,5,8}
                         {3,5,6,9}
                         {12,10,8,6}
   };

   int i, j;

   podw(smieci[0], 3*4);

   for (i=0; i<3; i++)
       {
           for (j=0; j<4; j++)
               printf("%5d", smieci[i][j]);
           putchar('\n');
       }

   return 0;

}

void podw(int tab[], int rozmiar)

{

   int i;

   for (i=0; i<rozmiar; i++)
       tab[i] *= 2;

}

Rozumiem, że do funkcji podw zostaje przekazany adres elementu smieci[0][0], nie rozumiem czemu w ostatniej instrukcji funkcji podw mnożymy WSKAŹNIKI a nie elementy tablicy przez 2 i czemu mnożymy tylko adresy od i do rozmiar czyli smieci[0][0], smieci[1][0], smieci[2][0] i smieci[3][0]? Jakim cudem mam zrozumieć, że chodzi tu o elementy (nie wskaźniki) smieci [0][0], smieci[0][1], smieci[0][2] oraz smieci [0][3]?

Jeżeli problem jest zbyt banalny i na forum nikt nie odpowiada na takie banały (a spotkałem takie forum już) to przepraszam i proszę usunąć ten temat.

1

Nie, nie, nie. Odczytaj to tak: do funkcji podw() jest przekazywana tablica. Funkcja przechodzi przez wszystkie jej elementy, podwajając ich wartości i aktualizując tę tablicę w miejscu. W ostatniej instrukcji nie mnożymy wskaźników. Zapis "x *= 2" jest równoznaczny z "x = x * 2".

0
Kumashiro napisał(a)

Nie, nie, nie. Odczytaj to tak: do funkcji podw() jest przekazywana tablica. Funkcja przechodzi przez wszystkie jej elementy, podwajając ich wartości i aktualizując tę tablicę w miejscu. W ostatniej instrukcji nie mnożymy wskaźników. Zapis "x *= 2" jest równoznaczny z "x = x * 2".

Ale w notatkach które robiłem podczas czytania książki dokładnie pisze, że język C nie pozwala przekazywać tablic jako argumentów funkcji, dalej w książce pisze, że nadaliśmy funkcji podw() argument smieci[0] co daje ADRES do elementu smieci[0][0] (czyli pierwszy argument formalny w podw() to wskaźnik). Nie da się całej tablicy przekazać. poza tym chodzi tutaj o tablicę tablic a nie jednowymiarową tablicę i ostatnia linijka funkcji podw() traktuje ją jako zwykłą jednowymiarową tablicę, a mnoży dwuwymiarową tablicę. Owszem, w main() przekazywany jest poprzez pętlę for wskaźnik do pierwszego elementu czyli tab[0], tab[1], co odpowiada tab[0][0] i tab[1][0], ale w błąd wprowadza tutaj ten zapis niby tab[i]=tab[i]*2 a ja tutaj widzę wskaźniki do elementów o wzorze tab[i][0], ponieważ nazwa tablicy to też adres do jej pierwszego elementu. Nadal jestem w błędzie :(

0

ten kod to chyba wcale nie jest taki banalny i wykorzystuje jakieś kruczki,albo jest wręcz błędny.

Funkcja podw mnoży elementy tablicy jednowymiarowej, a przekazuje się do niej wskaźnik na tablicę, czyli na pierwszy element tej tablicy (bo w C to to samo)
Pytanie czy coś takiego ma sens:

podw(smieci[0], 3*4); 

bo tu autor programu przekazuje jako parametr adres pierwszego wiersza tabeli i traktuje w funkcji tabelę dwuwymiarową jak tabelę jednowymiarową o wymiarach 3*4. Podejrzewam, że tak można w C.
Ale o wiele wygodniej i bardziej intuicyjnie byłoby przechodzić tą tablicę tak jak to się dzieje w kodzie, który ją wyświetla:

 for (i=0; i<3; i++)
       {
           for (j=0; j<4; j++)
               printf("%5d", smieci[i][j]);
           putchar('\n');
       }
0

Bo do funkcji nie przekazujesz tablicy. Ja tylko Ci napisałem jak masz to odczytywać żeby zrozumieć co się dzieje. Co przechowuje zmienna tablicowa? Wskaźnik do pierwszego elementu tablicy. Przekazując (umownie) tablicę do funkcji, przekazujesz wskaźnik.
Ostatnia linijka funkcji podw() nie mnoży elementów tablicy dwuwymiarowej. Przekazujesz do niej tablicę jednowymiarową, gdyż pamięć jest jednowymiarowa. Tak się składa, że macierze są mapowane w pamięci na tablicę jednowymiarową i funkcja to wykorzystuje. Ona ma podwoić wszystkie elementy i nie ma tu znaczenia ile jest wierszy i kolumn, więc ta informacja jest zbędna. Tablica {{2,4,5,8},{3,5,6,9},{12,10,8,6}} w pamięci jest zapisywana jako {2,4,5,8,3,5,6,9,12,10,8,6} i tak ją widzi funkcja. Upraszczając Twój program możesz zobaczyć o co chodzi:

#include <stdio.h>


void  show(int  *tab, int  size)
{
    int     cnt;


    for ( cnt = 0; cnt < size; cnt++ )
        printf("%d\n", tab[cnt]);
}


int  main(void)
{
    int     smieci[3][4] = {
                            {2,4,5,8},
                            {3,5,6,9},
                            {12,10,8,6}
                           };

    show(smieci[0], 3 * 4);
    return 0;
}
0

Nie wiem jak mogłem do tego dopuścić! Napisałem kod z dalszego przykładu książki, wybaczcie mi chodzi mi o ten:

#include <stdio.h>
 
void podw(int tab[], int rozmiar);
 
int main(void)
 
{
   static int smieci[3][4] = {
                         {2,4,5,8},
                         {3,5,6,9},
                         {12,10,8,6}
   };
 
   int i, j;
   
   for (i=0; i<3; i++)
       podw(smieci[i], 4);
 
   for (i=0; i<3; i++)
       {
           for (j=0; j<4; j++)
               printf("%5d", smieci[i][j]);
           putchar('\n');
       }
 
   return 0;
 
}
 
void podw(int tab[], int rozmiar)
 
{
 
   int i;
 
   for (i=0; i<rozmiar; i++)
       tab[i] *= 2;
 
}

Im bardziej uważnie staram się coś zrobić tym większy błąd mi wychodzi :/

0

Jeśli zamienisz błędne "podw(smieci[0], 4);" na poprawne "podw(smieci[i], 4);", to nic to nie zmieni. Do funkcji nadal przekazujesz jednowymiarową tablicę, z tą tylko różnicą, że nie wpychasz jej całej matrycy, lecz wiersz po wierszu.

0

Ten ostatni program jest OK, brakuje tylko przecinków.

static int smieci[3][4] = { { 2, 4, 5, 8},
                            { 3, 5, 6, 9},
                            {12,10, 8, 6} };
0

Wróciłem. Tak nad tym myślałem i wymyśliłem, że main() przekazuje podw() po prostu smieci[1][0], smieci[2][0] itd... i tablica już w funkcji podw() jest traktowana jako jednowymiarowa tablica (jeden odcinek który możemy nazwać wierszem macierzy) i można się do niej odwoływać poprzez jeden indeks (tab[i]) a nie dwa indeksy (tab[i][j])?

0

BTW, to Forum jest dobrym miejscem na zorientowanie się jaki temat z programowania aktualnie przerabiają uczelnie, gdyż chyba co drugi post jest o tym samym. W tym tygodniu mamy np. tablice wielowymiarowe i ich obsługa za pomocą funkcji...

0

Mnie nie przyciska żadna uczelnia ani szkoła, uczę się tego sam dla siebie, żeby kiedyś zacząć uczelnie. Btw... i tak tego nie rozumiem dokładnie, "nie widzę tego".

1

Polecam książkę "ANSI C" Kernighana i Ritchie. Kosztuje grosze, jest cieniutka, bardzo szybko się ją czyta i w genialnie prosty sposób omawia język C, m.in. tablice i wskaźniki.

0
Kumashiro napisał(a)

Polecam książkę "ANSI C" Kernighana i Ritchie. Kosztuje grosze, jest cieniutka, bardzo szybko się ją czyta i w genialnie prosty sposób omawia język C, m.in. tablice i wskaźniki.

Chyba żartujesz, wiesz ja nie wczułem się jeszcze w język C do tego stopnia, żeby młócić takie książki, myślę, że szkoła programowania Stephena Praty jest dobra na początek.

0

Huh? Co rozumiesz przez "takie książki"? K&R to nie jest techniczne opracowanie dla doświadczonych programistów. Jest ona napisana w sposób zrozumiały nawet dla totalnego laika, a co dopiero mówić o kimś, kto ma już jakieś doświadczenie w programowaniu. "ANSI C" powstała dla ludzi dopiero zaczynających programowanie w C (a nawet programowanie w ogólności) i prowadzi "za rączkę" przez wszystkie zagadnienia. Nie znajdziesz tam wyczesanych algorytmów, na widok których szczęka opada. Wszystko jest tam opisane bardzo prosto, bez niepotrzebnego lania wody, z ćwiczeniami, w sposób w jaki każda książka do nauki języka programowania powinna być tworzona. Mam tę książkę od 13 lat i wciąż chętnie do niej wracam co jakiś czas :)
No i czy może być lepsze źródło wiedzy o języku C niż książka napisana przez jego twórcę?

0

Zobacz sobie te stronke http://4programmers.net/C/Operatory . Zrozumiesz co oznacza *=.

Na poczatku omowmy sobie funkcje podw().

 
void podw(int *tab, int rozmiar)
{
   int i;
   for (i=0; i<rozmiar; i++)
       *tab[i] *= 2;
}

Funkcja przyjmuje 2 argumenty:

  1. Adres aktualnie edytowanej kolumny
    
  2. ilosc elementow w kazdej kolumnie 
    

Opis dzialania:
Tworzysz zmienna pomocnicza i. Nastepnie tworzysz petle for(). Wewnatrz petli liczac od zerowego elementu w danej kolumnie do ostatniego elemntu w podanej kolumnie podwajasz kazda wartosc "tab[i] *= 2". Gdy twoja liczba w pewnej komorce jest podwojona, zwiekszasz liczbe i o 1 i wracasz na poczatek petli. Takim sposobem mozesz podwoic kolejna liczbe i kolejna az dojdziesz do konca petli.
Gdy wyjdziesz z petli wychodzisz z funkcji.

void podw(int tab[], int rozmiar) poprawilem na void podw(int *tab, int rozmiar).
Wedlug mnie tak powinno byc poniewac przy int tab[] tworzymy nowa tablice zawierajaca te same elementy lecz posiadajaca inny adres i ta zmienna bedzie kasowana wraz z wyjsciem z funkcji. Jesli zle mowie poprawcie mnie.

 
int main(void)
{
   static int smieci[3][4] = {
                         {2,4,5,8},
                         {3,5,6,9},
                         {12,10,8,6}
                                    };
   int i, j;
 
   for (i=0; i<3; i++)
   {
       podw(&smieci[i][0], 4); //pierwszy argument to adres pierwszej komorki **i** kolumny.
   }   
   for (i=0; i<3; i++)
       {
           for (j=0; j<4; j++)
               printf("%5d", smieci[i][j]);
           putchar('\n');
       }
 
   return 0;
 
}

W funkcji main() najpierw tworzysz tablice dwuwymiarowa o rozmiarach [3][4]. Jej elementy sa liczone w sposob:
[0][0], [0][1], [0][2], [0][3], [1][0], [1][1], [1][2], [1][3], [2][0], [2][1], [2][2], [2][3].
Tak samo wyglada przydzial pamieci dla twoich elementow tablicy.

Nastepnie tworzysz 2 zmienne pomocnicze: i i j.
W pierwszej petli for() wykonujemy funkcje podw() dla kazdej kolumny przekazujac adresy komorek [0][0], [1][0], [2][0].
Po wykonaniu tej petli nasze liczby w tablicy beda podwojone.
W drugiej petli wyswietlasz liczby i wychodzisz z programu.

Nie sprawdzalem tego kodu bo chwilowo ukradli mi kompilator wiec jak cos poprawiajcie :)

0
gswidwa napisał(a)

Tworzysz zmienna pomocnicza i. Nastepnie tworzysz petle for(). Wewnatrz petli liczac od zerowego elementu w danej kolumnie do ostatniego elemntu w podanej kolumnie podwajasz kazda wartosc "tab[i] *= 2". Gdy twoja liczba w pewnej komorce jest podwojona, zwiekszasz liczbe i o 1 i wracasz na poczatek petli. Takim sposobem mozesz podwoic kolejna liczbe i kolejna az dojdziesz do konca petli.

OK, tylko że funkcja nie widzi żadnych kolumn. Ona przechodzi po elementach tablicy. Rozróżnienie na kolumny zachodzi wyżej.

gswidwa napisał(a)

void podw(int tab[], int rozmiar) poprawilem na void podw(int *tab, int rozmiar).
Wedlug mnie tak powinno byc poniewac przy int tab[] tworzymy nowa tablice zawierajaca te same elementy lecz posiadajaca inny adres i ta zmienna bedzie kasowana wraz z wyjsciem z funkcji. Jesli zle mowie poprawcie mnie.

Źle mówisz. "tab[]" oraz "*tab" są tu równoznaczne i tworzą nową zmienną lokalną. Nie zachodzi tutaj kopiowanie danych (poza wartością wskaźnika). Do danych odwołujesz się przez wskaźnik, zatem nie modyfikujesz jego wartości, tylko dane na które wskazuje.

gswidwa napisał(a)

podw(&smieci[i][0], 4); //pierwszy argument to adres pierwszej komorki i kolumny.


Dlaczego takie kombinacje stosujesz? W `smieci[i]` masz już wymaganą wartość (wskaźnik do pierwszego elementu wiersza). Przy powyższym zapisie najpierw odczytujesz zmienną dereferencjując wskaźnik (`smieci[i][0]`), a następnie znowu wyciągasz ten sam wskaźnik (`&`).
0
Kumashiro napisał(a)

Huh? Co rozumiesz przez "takie książki"? K&R to nie jest techniczne opracowanie dla doświadczonych programistów. Jest ona napisana w sposób zrozumiały nawet dla totalnego laika, a co dopiero mówić o kimś, kto ma już jakieś doświadczenie w programowaniu. "ANSI C" powstała dla ludzi dopiero zaczynających programowanie w C (a nawet programowanie w ogólności) i prowadzi "za rączkę" przez wszystkie zagadnienia. Nie znajdziesz tam wyczesanych algorytmów, na widok których szczęka opada. Wszystko jest tam opisane bardzo prosto, bez niepotrzebnego lania wody, z ćwiczeniami, w sposób w jaki każda książka do nauki języka programowania powinna być tworzona. Mam tę książkę od 13 lat i wciąż chętnie do niej wracam co jakiś czas :)
No i czy może być lepsze źródło wiedzy o języku C niż książka napisana przez jego twórcę?

Zwracam honor. Po prostu wszedłem za daleko do rozdziału o tablicach i przeczytałem jakiś trochę skomplikowany program. Przeczytałem od początku ten rozdział i jest ok.

I nie przypominajcie mi co znaczy *= bo dobrze wiem co znaczy ;)

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