C - Tablica statyczna dwuwymiarowa w funkcji

C - Tablica statyczna dwuwymiarowa w funkcji
B4
  • Rejestracja:ponad 3 lata
  • Ostatnio:ponad rok
  • Postów:12
0

Cześć, mam problem z działaniem na tablicy statycznej dwuwymiarowej w funkcji.
W poniższym kodzie próbuje zrozumieć działanie funkcji i wskaźników, docelowo mam napisany inny kod ale cały w main i chcę go poprawić. Zależy mi na umieszczeniu wzoru w funkcji, wzór ten wykorzystuje dwie tablice dynamiczne, tablice statyczną i kilka zmiennych. Wynikiem są zmienione wartości tylko w jednej tablicy dynamicznej. Nic nie zwracam bo wyniki z tej tablicy są potrzebne w dalszych obliczeniach.
Rozumiem jak wrzucić do funkcji zmienną jako kopie, zmienną jako wskaźnik, tablice dynamiczną dwuwymiarową jako wskaźnik, ale nie wiem jak umieścić i korzystać z:

  • tablicy statycznej dwuwymiarowej jako kopia
  • tablicy statycznej dwuwymiarowej jako wskaźnik
  • tablicy dynamicznej dwuwymiarowej jako kopia

Jako kopia rozumiem działanie na kopiach wartości, czyli po za funkcją nic się nie zmienia, natomiast jako wskaźnik rozumiem działanie na adresach wartości, czyli po wyjściu z funkcji i nie zwracaniu niczego zmiany są widoczne bo bazuje na adresach.

W poniższym kodzie mogę działać na tablicy dynamicznej, jednak nie wiem jak zaimplementować tablicę statyczną w funkcji. Ma ktoś czas pomóc?

Jestem samoukiem więc wszelkie uwagi odnośnie sposobu pisania kodu są bardzo mile widziane, dopiero zaczynam więc warto porzucić złe nawyki.

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

void test_dynamiczna(int *p, int **tablica_dyn);
void test_statyczna(int *p, int **tablica_stat);

int main(){
    int p = 5;      // do mnożenia
    int w1 = 3;     // liczba wierszy
    int k = 2;      // liczba kolumn
    int i, j;       // do for
    int o = 1;      // do zapisywania

    // Alokacja pamięci
    int **tablica_dyn;
    tablica_dyn = (int **)malloc(w1 * sizeof(int *));
    for (i = 0; i < w1; i++){
        tablica_dyn[i] = (int *)malloc(k * sizeof(int));
    }
    // Zapisanie wartości do tablica_dyn
    for (i = 0; i < w1; i++){
        for (j = 0; j < k; j++){
            tablica_dyn[i][j] = o;
            o++;
        }
    }
    // Zapisanie wartości do tablica_stat
    int tablica_stat[3][2] = {
        {1 ,2},
        {3, 4},
        {5, 6}
    };

    
    // Wyświetlenie tablica_dyn przed zmianą
    for (i = 0; i < w1; i++){
       for (j = 0; j < k; j++){
          printf("%d  ", tablica_dyn[i][j]);
       }
       printf("\n");
    }
    printf("\n");
    // Wyświetlenie tablica_stat przed zmianą
    for (i = 0; i < w1; i++){
       for (j = 0; j < k; j++){
          printf("%d  ", tablica_stat[i][j]);
       }
       printf("\n");
    } 
    printf("\n\n");

    test_dynamiczna(&p, tablica_dyn);
    test_statyczna(&p, tablica_stat);


    // Wyświetlenie tablica_dyn po zmianach
    for (i = 0; i < w1; i++){
       for (j = 0; j < k; j++){
          printf("%d  ", tablica_dyn[i][j]);
       }
       printf("\n");
    }
    printf("\n");
    // Wyświetlenie tablica_stat po zmianach
    for (i = 0; i < w1; i++){
       for (j = 0; j < k; j++){
          printf("%d  ", tablica_stat[i][j]);
       }
       printf("\n");
    } 


    // Zwalnianie pamięci
    for (i = 0; i < w1; i++){
        free(tablica_dyn[i]);
    }
    free(tablica_dyn);
    tablica_dyn = NULL;

    return 0;
}


void test_dynamiczna(int *p, int **tablica_dyn){
    tablica_dyn[1][1] *= *p;
}

void test_statyczna(int *p, int **tablica_stat){
    tablica_stat[1][1] *= *p;
}
enedil
  • Rejestracja:ponad 11 lat
  • Ostatnio:4 dni
  • Postów:1027
1

W języku C, nie da się przekazać tablicy przez wartość (a tym bardziej tablicy zagnieżdżonej).

Możesz przez wartości przekazywać natomiast struktury. Np.

Kopiuj
struct MojaTablica {
    int tab[3][2];
};

Ale w ten sposób nie poradzisz sobie z tablicami tworzonymi dynamicznie, gdyż one tak naprawdę nie są tablicami (tylko wskaźnikami na ileś elementów).
To co możesz zrobić, to np. funkcję klonująca daną tablicę dynamiczną (tutaj zwracamy int**), albo funkcję która w dane miejsce w pamięci kopiuje tablicę zablokowaną automatycznie (którą ty nazywasz alokacją statyczną, co nie jest poprawne).

Nitpicks: malloc może się nie powieść. ZAWSZE należy sprawdzać czy wynik malloca to NULL, i zareagować na to jakoś. Wyświetlanie (obu tablic) można zrealizować za pomocą dedykowanej (jednej) funkcji.

JV
  • Rejestracja:ponad 6 lat
  • Ostatnio:około 2 miesiące
  • Postów:242
0

Jak się przekazuje tablicę jednowymiarową do funkcji to następuje degradacja do wskaźnika. W przypadku tablic dwu i więcej wymiarowych degradacja do wskaźnika następuje tylko dla jednego wymiaru. Dla funkcji statycznej musisz więc podać jeden wymiar i musi się on zgadzać deklarację tablicy w funkcji wywołującej:

Kopiuj
void test_statyczna(int *p, int (*tablica_stat)[2]){
    tablica_stat[1][1] *= *p;
}

tablica statyczna wielowymiarowa ma w pamięci po kolei ułożone wartości więc przekaż jako wskaźnik, jej wymiary i licz offset:

Kopiuj
void test_statyczna(int* p, int* tablica_stat, int xdim, int ydim, int x, int y) {
    int offset = xdim*y + x; // <- coś w tym stylu, ale nie testowane z powodu chwilowego spadku IQ :-)
    tablica_stat[offset] *= *p;
}
MarekR22
Moderator C/C++
  • Rejestracja:około 17 lat
  • Ostatnio:4 minuty
1

Uważaj na terminologię, bo słowo "statyczne zmienne" ma znaczenie inne niż ci się wydaje.

Niestety w C tablice kopiowane są wyłącznie jawnie.
Tablice przekazywane są jako wskaźnik, albo wręcz ulegają degradacji do wskaźnika (decay).

Każdy komentarz jak np ten:

Kopiuj
// Wyświetlenie tablica_dyn przed zmianą

tak naprawdę jest zaproszeniem do wydzielenia funkcji z kodu.
Komentarze lubią się przedawniać (ktoś poprawi kod, ale nie komentarz), więc lepiej wyrazić coś w kodzie zamiast w komentarzu.

Kopiuj
wyswietl_tablic_dyn(tablica_dyn, w1, k);

powyższe mówi co komentarz, a zstępuje nie tylko komentarz, ale też kod.


Jeśli chcesz pomocy, NIE pisz na priva, ale zadaj dobre pytanie na forum.
JM
Dodam, że funkcji jest 99.9% NIEMAL obojętne, czy dostaje tablicę statyczną, czy dynamiczną, i ich podwajanie w normalnym życiu nie ma sensu - chybą że dla nauki. (Przynajmniej jednowymiarową)
MarekR22
Moderator C/C++
  • Rejestracja:około 17 lat
  • Ostatnio:4 minuty
1

Najlepiej pokazuje się jak powinien wyglądać kod przez przykład:
https://godbolt.org/z/sa1z77TYn

Zwróć uwagę, że:

  1. wszystko jest po angielsku (zostawiłem parę komentarzy po polsku)
  2. nazwy symboli mówią co się dzieje, więc większość komentarzy jest zbędna
  3. Wydzielone są małe funkcje k†óre łątwo ogarnąć
  4. zmienne dotyczące macierzy zostały zamknięte w jednej strukturze, w przez co łatwiej się ogarnia kod (mniej argumentów do przekazania
  5. niepotrebnie dodajesz cast wartości zwracanej z malloc. TO jest potrzeben w C++, w C void* domyślnie konwertuje się do wskaźnika na dowolny typ

Jeszcze można to poprawiać (np ten niezrozumiały wskaźnik przy testowaniu mnożenia).

Użyłem też VLA z C99 (jako argument funkcji).

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

typedef struct MatrixInt {
    int rows;
    int columns;
    int **items;
} MatrixInt;

MatrixInt MatrixIntMake(int r, int c) {
    int i;
    MatrixInt result = {};
    result.items = malloc(r * sizeof(result.items[0]));
    if (!result.items) return result;

    for (i = 0; i < r; i++) {
        result.items[i] = malloc(c * sizeof(result.items[0][0]));
    }
    result.rows = r;
    result.columns = c;

    return result;
}

void MatrixIntFree(MatrixInt m) {
    int i;
    for (i = 0; i < m.rows; i++) {
        free(m.items[i]);
    }
    free(m.items);
}

void MatrixIntFillIncresing(MatrixInt m, int start) {
    int i, j;
    for (i = 0; i < m.rows; i++) {
        for (j = 0; j < m.columns; j++) {
            m.items[i][j] = start;
            start++;
        }
    }
}

void MatrixIntPrintInto(FILE *f, MatrixInt m) {
    int i, j;
    for (i = 0; i < m.rows; i++) {
        for (j = 0; j < m.columns; j++) {
            fprintf(f, "%d  ", m.items[i][j]);
        }
        fprintf(f, "\n");
    }
    fprintf(f, "\n");
}

void MatrixIntTest(MatrixInt m, int *multiplicationFactor) {
    if (m.rows > 1 && m.columns > 1) m.items[1][1] *= *multiplicationFactor;
}

void testIntVLA(int columns, int tablica_stat[][columns],
                int *multiplicationFactor) {
    tablica_stat[1][1] *= *multiplicationFactor;
}

void MatrixIntPrint(MatrixInt m) { MatrixIntPrintInto(stdout, m); }

void printIntVLA(int rows, int columns, int m[][columns]) {
    int i, j;
    for (i = 0; i < rows; i++) {
        for (j = 0; j < columns; j++) {
            printf("%d  ", m[i][j]);
        }
        printf("\n");
    }
    printf("\n");
}

int main() {
    int multiplicationFactor = 5; 
    int rowsCount = 3;
    int columnsCount = 2;

    MatrixInt tablica_dyn;
    tablica_dyn = MatrixIntMake(rowsCount, columnsCount);

    MatrixIntFillIncresing(tablica_dyn, 1);

    int tablica_stat[3][2] = {{1, 2}, {3, 4}, {5, 6}};

    MatrixIntPrint(tablica_dyn);

    printIntVLA(3, 2, tablica_stat);

    MatrixIntTest(tablica_dyn, &multiplicationFactor);
    testIntVLA(2, tablica_stat, &multiplicationFactor);

    // Wyświetlenie tablica_dyn po zmianach
    MatrixIntPrint(tablica_dyn);
    printIntVLA(3, 2, tablica_stat);

    MatrixIntFree(tablica_dyn);

    return 0;
}

Jeśli chcesz pomocy, NIE pisz na priva, ale zadaj dobre pytanie na forum.
edytowany 3x, ostatnio: MarekR22
B4
  • Rejestracja:ponad 3 lata
  • Ostatnio:ponad rok
  • Postów:12
0

Wielkie dzięki za pomoc, struktury znam tylko z nazwy więc zabieram się za ich naukę a potem przerobienie kodów od Was. Dzięki też za ogólne uwagi odnośnie pisanie kodu.
Chciałbym jeszcze zapytać o Twój kod @MarekR22, piszę w VSC i zapisuje wszystko jako .c jednak po skopiowaniu kodu i odpaleniu wskazuje mi 3 problemy:

  • oczekiwano wyrażenia C/C++ (29) [12, 25]
  • parametr jest niedozwolony C/C++ (411) [58, 49]
  • parametr jest niedozwolony C/C++ (411) [65, 49]

Czym to jest spowodowane?

MarekR22
VSC to tylko IDE (Integrated Development Environment). Jaki masz zainstalowany (skonfigurowany) w nim kompilator? Z jakimi ustawieniami budujesz?
MarekR22
Moderator C/C++
  • Rejestracja:około 17 lat
  • Ostatnio:4 minuty
0
B4rte napisał(a):

Wielkie dzięki za pomoc, struktury znam tylko z nazwy więc zabieram się za ich naukę a potem przerobienie kodów od Was. Dzięki też za ogólne uwagi odnośnie pisanie kodu.

Chciałbym jeszcze zapytać o Twój kod @MarekR22, piszę w VSC i zapisuje wszystko jako .c jednak po skopiowaniu kodu i odpaleniu wskazuje mi 3 problemy:

  • oczekiwano wyrażenia C/C++ (29) [12, 25]
  • parametr jest niedozwolony C/C++ (411) [58, 49]
  • parametr jest niedozwolony C/C++ (411) [65, 49]

Czym to jest spowodowane?

Dobra już wiem o co chodzi. Twój kompilator to MSVC.
Z powodu wstecznej kompatybilności Microsoft zdecydował, że będzie wspierał tylko C94, więc nie ma VLA wprowadzonych w C99.

Bez C99 nie ma prostej metody zaimplementowania: testIntVLA i printIntVLA :(.
W twoim przypadku trzeba sztywno wprowadzić wielkość tablic.

Kopiuj
// było testIntVLA ale VLA jest nidostępne
void testIntConstSizeArray(int tablica_stat[][2],
                int *multiplicationFactor) {
    tablica_stat[1][1] *= *multiplicationFactor;
}

A błąd dla linii 12 łatwo naprawić (wywal = {} i jawnie wszystko ustaw na zero).


Jeśli chcesz pomocy, NIE pisz na priva, ale zadaj dobre pytanie na forum.
edytowany 4x, ostatnio: MarekR22
several
  • Rejestracja:ponad 15 lat
  • Ostatnio:około godziny
1

Sytuacja staje się prostsza, gdy uświadomisz sobie, że tak na prawdę nie potrzebujesz dwuwymiarowej tablicy, bo jednowymiarowa pokryje wszystkie potrzeby. Sporo problemów odpada, do tego jeden malloc jeden free. Pozowliłem sobie lekko zmodyfikować kod @MarekR22

Kopiuj
typedef struct MatrixInt {
    int rows;
    int columns;
    int *items;
} MatrixInt;

MatrixInt MatrixIntMake(int r, int c) {
    MatrixInt result = {
       .rows = r,
       .columns = c,
    };

    result.items = (int*)malloc(r * c * sizeof *result.items);

    return result;
}

void MatrixIntFree(MatrixInt m) {
    free(m.items);
}

int MatrixGetValue(MatrixInt m, int x, int y)
{
   return m.items[y * m.columns + x);
}

void MatrixIntPrint(MatrixInt m)
{
   int row = 0, col = 0;
   for(; row < m.rows; ++row)
   {
      for(;col < m.columns; ++col)
         printf("%i ", MatrixGetValue(m, col, row));
   
      printf("\n");
      col = 0;
   }
}


edytowany 1x, ostatnio: several
MarekR22
da się popchnąć to jeszcze dalej: https://wandbox.org/permlink/ktsqg27KDrddnreY ale nie chciałem mącić mu w głowie.
several
fajny przykład, wszystko na stercie
Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)