• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

Język C. Dynamiczna alokacja struktury. Lista powiązana i macierz.

VPS Starter Arubacloud
0 głosów
808 wizyt
pytanie zadane 1 stycznia 2020 w C i C++ przez DamianW Bywalec (2,080 p.)

Witam. Mam pytanko. Ktoś wie jak zaalokować daną poniżej strukturę w języku C za pomocą calloc()?Struktura ta ma być strukturą typu forward_list ,która przechowuje typ Matrix.Jak widać jednym  z pól tej struktury(node) jest właśnie Matrix. W samej strukturze Matrix jest pole double**body, które każdorazowo alokuje się dynamicznie zależnie od rozmiaru macierzy.Jak w ogóle zabrać się za alokację pamięci węzłów tej listy powiązanej w stylu języka C (wykorzystując calloc() lub malloc())? Głównie chodzi mi o to jak zapewnić strukturze node odpowiednią ilość pamięci.

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

typedef struct{
    size_t col_size;
    size_t ver_size;
    double** body;
}Matrix;

void initMatrix(Matrix* mtx,const size_t ver_size,const size_t col_size){
    mtx->col_size = col_size;
    mtx->ver_size = ver_size;
    mtx->body = (double**)calloc(mtx->ver_size,sizeof(double*));
    for (size_t i = 0 ; i < mtx->ver_size;i++)
        mtx->body[i] = (double*)calloc(mtx->col_size,sizeof(double));
    for (size_t i = 0 ; i < mtx->ver_size;i++)
        for (size_t j = 0 ; j < mtx->col_size;j++)
            mtx->body[i][j] = 0.0;
}

struct node{
    int index;
    Matrix mtx;
    node* next;
};


struct list{
    node* first;
    node* actual;
    size_t nmbr;
};

void initList(list* list){
    list->nmbr = 0;
    list->first = NULL;
    list->actual = NULL;
}

size_t returnMtxSize(const size_t ver_size,const size_t col_size){
    return 2*sizeof(size_t) + ver_size*col_size;    
}

node* makeNewNode(const int index,const size_t ver_size,const size_t col_size){
    node* node ;
    node = (node*)malloc(returnMtxSize(ver_size,col_size));//-< jak zając odpowiednią 
    // ilość pamięci?
}

 

2 odpowiedzi

+1 głos
odpowiedź 2 stycznia 2020 przez mokrowski Mędrzec (155,460 p.)
wybrane 2 stycznia 2020 przez DamianW
 
Najlepsza

1. Staraj się stosować idiom alokacji z C:

struct X * x = malloc(sizeof(*x));

Pozwala to później na poprawianie mniejszej ilości kodu jeśli struktura się zmienia.

2. W C nie rzutuje się wyniku działania malloc(..), calloc(..) czy innych tego typu funkcji. Zwracają one void * który jest automatycznie konwertowany na docelowy typ.

3. Zastanów się poważnie jak będzie wyglądała macierz po takiej alokacji jak to zrobiłeś (i być może Cię uczono). Jest to tablica wskaźników na "rozstrzelone po adresach pamięci przestrzenie". Taka macierz jest obsługiwana bardzo wolno ze względu na słabą trafialność w cache. O wiele lepiej jest alokować 1 przestrzeń pamięci i "zbudować tablicę wskaźników" (bo jak rozumiem proszono Cię o obsługę double ** body .. czyli "dwie gwiazdki"). Takie alokowanie jest obecne we wszystkich bibliotekach obsługi macierzy (nawet w Python który nie zawsze w każdym aspekcie zwraca na to uwagę). Przykład:

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

typedef struct {
    size_t col_size;
    size_t ver_size;
    double * * body;
} Matrix;

void initMatrix(Matrix * mtx, const size_t ver_size, const size_t col_size) {
    mtx->col_size = col_size;
    mtx->ver_size = ver_size;
    // Alokowanie z zerowaniem bufora na dane
    double * data_buff = calloc(mtx->ver_size * mtx->col_size, sizeof(*data_buff));
    // Alokowanie tablicy wskaźników na wiersze dla tablicy 2D
    mtx->body = malloc(mtx->ver_size * sizeof(*(mtx->body)));
    // Budowanie wskaźników na poszególne wiersze w ramach bufora data_buff
    for(size_t i = 0; i < mtx->ver_size; ++i) {
        mtx->body[i] = data_buff + mtx->col_size * sizeof(**(mtx->body)) * i;
    }
}

void destroyMatrix(Matrix * mtx) {
    double * buff = *(mtx->body);
    free(mtx->body);
    free(buff);
}

void showMatrix(const Matrix * mtx) {
    printf("Matrix: rows = %zu, cols = %zu\n", mtx->ver_size, mtx->col_size);
    for(size_t row = 0; row < mtx->ver_size; ++row) {
        for(size_t col = 0; col < mtx->col_size; ++col) {
            printf("%10.4f ", mtx->body[row][col]);
        }
        putchar('\n');
    }
}

int main(void) {
    Matrix * mtx = malloc(sizeof(*mtx));
    initMatrix(mtx, 5, 10);
    // Zestaw prostackich testów
    mtx->body[0][0] = 42.4242;
    mtx->body[4][9] = 3.1415;
    mtx->body[4][0] = 2.72;
    mtx->body[0][9] = 1410.2133;

    showMatrix(mtx);

    destroyMatrix(mtx);
    free(mtx);

    return 0;
}

4. W przypadkach gdy to jest nieistotne, preferuj preinkrementację przed postinkrementacją. Nie sugeruj czytelnikowi że poprzednia wartość zmiennej ma znaczenie (argument "to zrobi sam kompilator", tu nie jest istotny bo odbiorcą jest człowiek).

5. Dobrze jest w C przemyśleć fakt kreacji struktur. Jeśli zrobisz to dobrze, dostaniesz od razu dobrą obsługę błędów. Tak mój przykład wyżej jak i Twój, nie sprawdza poprawności alokowania pamięci. A problem nie leży w "niedbalstwie" (choć może trochę :-) ), a raczej w złamaniu zasady że alokowanie i zwalnianie zasobu, powinno odbywać się na tym samym poziomie abstrakcji. Tu prosta poprawka poprzedniego kodu. Będzie obsługa i błędu i lepsza alokacja:

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

typedef struct {
    size_t col_size;
    size_t ver_size;
    double * * body;
} Matrix;

void initMatrix(Matrix * * mtx_ptr, const size_t ver_size, const size_t col_size) {
    Matrix * mtx = malloc(sizeof(Matrix));
    if(NULL == mtx) {
        goto ERR_0;
    }
    *mtx_ptr = mtx;
    mtx->col_size = col_size;
    mtx->ver_size = ver_size;
    // Alokowanie z zerowaniem bufora na dane
    double * data_buff = calloc(mtx->ver_size * mtx->col_size, sizeof(*data_buff));
    if(NULL == data_buff) {
        goto ERR_1;
    }
    // Alokowanie tablicy wskaźników na wiersze dla tablicy 2D
    mtx->body = malloc(mtx->ver_size * sizeof(*(mtx->body)));
    if(NULL == mtx->body) {
        goto ERR_2;
    }
    // Budowanie wskaźników na poszególne wiersze w ramach bufora data_buff
    for(size_t i = 0; i < mtx->ver_size; ++i) {
        mtx->body[i] = data_buff + mtx->col_size * sizeof(**(mtx->body)) * i;
    }

    return;

// Obsługa błędów alokacji
ERR_2:
    fprintf(stderr, "Zła alokacja wskaźników na wiersze!\n");
    free(data_buff);
ERR_1:
    fprintf(stderr, "Zła alokacja bufora na dane!\n");
    free(mtx);
ERR_0:
    fprintf(stderr, "Zła macierzy!\n");
    *mtx_ptr = NULL;
    return;
}

void destroyMatrix(Matrix * mtx) {
    double * buff = *(mtx->body);
    free(mtx->body);
    free(buff);
    free(mtx);
}

void showMatrix(const Matrix * mtx) {
    printf("Matrix: rows = %zu, cols = %zu\n", mtx->ver_size, mtx->col_size);
    for(size_t row = 0; row < mtx->ver_size; ++row) {
        for(size_t col = 0; col < mtx->col_size; ++col) {
            printf("%10.4f ", mtx->body[row][col]);
        }
        putchar('\n');
    }
}

int main(void) {
    Matrix * mtx;
    initMatrix(&mtx, 5, 10);
    if(NULL == mtx) {
        fprintf(stderr, "Problem z inicjalizacją macierzy!\n");
        return EXIT_FAILURE;
    }
    // Zestaw prostackich testów
    mtx->body[0][0] = 42.4242;
    mtx->body[4][9] = 3.1415;
    mtx->body[4][0] = 2.72;
    mtx->body[0][9] = 1410.2133;

    showMatrix(mtx);

    destroyMatrix(mtx);

    return EXIT_SUCCESS;
}

6. Podobną obsługę alokacji warto zrobić w makeNewNode(...). Tu będzie prościej (bo nie będzie 2 x wskaźnika), bo funkcja zwraca wskaźnik, więc może w przypadku błędu, zwrócić NULL.

komentarz 2 stycznia 2020 przez DamianW Bywalec (2,080 p.)
Czy sizeof(*data_buff) jest tym samym ,co sizeof(double) ? Jeżeli nie ,to dlaczego ?Dodatkowo dziwi mnie zapis *mtx_ptr = mtx; . Dlaczego *mtx_ptr nie ma nazwy typu przed sobą ?
komentarz 2 stycznia 2020 przez mokrowski Mędrzec (155,460 p.)

Czy sizeof(*data_buff) jest tym samym ,co sizeof(double) ?

Tak. Są tym samym. Lepiej jednak stosować ten idiom. Tym bardziej (czego już tu nie robiłem), że w większości kompilatorów zawodowo napisał byś tak:

typeof(*(mtx->body)) data_buff = calloc(mtx->ver_size * mtx->col_size, sizeof(*data_buff));

A to już zupełnie uniezależnia Cię od typu danych w Matrix i pozwala zmienić go bez zmiany programu.

Niestety typeof(...) jest poza standardem C. Choć wszystkie kompilatory mają tę konstrukcję, nie podawałem jej na początku bo... jest poza standardem :)

Dlaczego *mtx_ptr nie ma nazwy typu przed sobą ?

Bo mtx_ptr jest argumentem funkcji i z racji tego że jest podwójnym wskaźnikiem na double, po pojedynczym wyłuskaniu (*mtx_ptr), wskazuje na pojedynczy wskaźnik na double.

Jeśli do funkcji w C chcesz przekazać wskaźnik który ma być zmodyfikowany, przekazujesz go przez podwójny wskaźnik.

+1 głos
odpowiedź 1 stycznia 2020 przez Patrycjerz Mędrzec (192,340 p.)
Obiekt struktury `node` ma stały rozmiar, czyli `sizeof(int) + sizeof(Matrix) + sizeof(node*)`. To, na co wskazują wskaźniki, czyli na pamięć dynamiczną, nie zmienia rozmiaru obiektu. Aby więc uzyskać rozmiar node'a, użyj `sizeof(node)`.

Tak w ogóle, zerowanie macierzy w pętlach `for` nie ma sensu, jeśli używasz funkcji `calloc`. Także zerowanie wskaźników `double*` nie ma sensu, bo zaraz po ich utworzeniu są nadpisywane.

Podobne pytania

0 głosów
1 odpowiedź 159 wizyt
pytanie zadane 19 kwietnia 2021 w C i C++ przez MrChick888 Obywatel (1,020 p.)
0 głosów
1 odpowiedź 212 wizyt
0 głosów
1 odpowiedź 562 wizyt
pytanie zadane 19 listopada 2018 w C i C++ przez Roman1212 Początkujący (460 p.)

92,453 zapytań

141,262 odpowiedzi

319,088 komentarzy

61,854 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Akademia Sekuraka 2024 zapewnia dostęp do minimum 15 szkoleń online z bezpieczeństwa IT oraz dostęp także do materiałów z edycji Sekurak Academy z roku 2023!

Przy zakupie możecie skorzystać z kodu: pasja-akademia - użyjcie go w koszyku, a uzyskacie rabat -30% na bilety w wersji "Standard"! Więcej informacji na temat akademii 2024 znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...