Elektroda.pl
Elektroda.pl
X

Search our partners

Find the latest content on electronic components. Datasheets.com
Elektroda.pl
Please add exception to AdBlock for elektroda.pl.
If you watch the ads, you support portal and users.

[C] Funkcja malloc i wskaźniki

dtoretto14 30 Apr 2012 12:55 4074 7
  • #1
    dtoretto14
    Level 10  
    Witam. Mam pewien problem z funkcją malloc. Przeczytałem z różnych źródeł wiele informacji na jej temat jednak nie potrafię jej do końca zrozumieć. Otóż na początek otrzymałem takie zadanie :
    Code:
    "Napisać program, który pozwoli zapamiętać w postaci tablicy struktur dane o 10 użytkownikach w postaci imię, nazwisko, data urodzenia. Przedstawić działanie programu na przykładowych danych wypisanych na ekranie."

    To udało mi się zrobić, tutaj wklejam kod :
    Code: c
    Log in, to see the code

    Teraz mam zedytować ten program według polecenia kolejnego zadania :
    Code:
    Napisać program analogiczny do pkt 6.3 z ta różnicą, że dane powinny być przechowywane w tablicy stu elementowej przechowującej wyłącznie adresy do danych, a dane będą alokowane w sposób dynamiczny w miarę potrzeb użytkownika.

    Czyli teraz muszę użyć funkcji malloc do alokowania pamięci w sposób dynamiczny a do tablicy zapisać tylko adresy do danych.

    Próbowałem zrealizować to na zasadzie usunięcia całej struktury na początku i utworzeniem tablicy 100 elementowej i teraz nie wiem jak zrobić dalszą część polecenia, czyli pobierać dane scanf'em wpisywane z klawiatury, otrzymać ich adres i zaalokować dynamicznie. Mógłby mnie ktoś naprowadzić lub powiedzieć jak to zrealizować?
  • Helpful post
    #2
    winuser2
    Level 17  
    1. Pobierasz rozmiar tablicy przy użyciu scanf("%d", &rozmiar) zmienna rozmiar jest typu całkowitego.

    2. Przydzielasz pamięć dla tablicy struktur. Zakładamy, że wcześniej zdefiniowałeś typ 'Struktura'.

    Code: c
    Log in, to see the code
  • Helpful post
    #3
    krru
    Level 33  
    dtoretto14 wrote:
    Witam. Mam pewien problem z funkcją malloc. Przeczytałem z różnych źródeł wiele informacji na jej temat jednak nie potrafię jej do końca zrozumieć. Otóż na początek otrzymałem takie zadanie :
    Code:
    "Napisać program, który pozwoli zapamiętać w postaci tablicy struktur dane o 10 użytkownikach w postaci imię, nazwisko, data urodzenia. Przedstawić działanie programu na przykładowych danych wypisanych na ekranie."

    To udało mi się zrobić, tutaj wklejam kod :
    Code:
    #include <stdio.h>
    
    #include <stdlib.h>

    struct dane{
    char imie[10][30];
    char nazwisko[10][30];
    int dzien[10];
    char miesiac[10][20];
    int rok[10];
    }str;




    Nie najszcześliwiej zacząłeś.

    Powinna być definicja pojedynczej struktury i potem tabelka struktur.

    Code: c
    Log in, to see the code



    W drugim podejściu trzeba zmienić tabelkę struktur na tabelkę wskaźników, ale by to miało sens, trzeba warto by przewidzieć operację wstawiania nowego elementu i usuwania wybranego.
  • Helpful post
    #4
    Krzysztof Gustaw
    Level 23  
    Witam!
    Hmm... Na początku to trochę rozwlekłeś strukturę. Niepotrzebnie pola: imię, nazwisko, dzień, miesiąc, rok zdefiniowałeś jako tablice dwuwymiarowe a konkretnie niepotrzebnie dodałeś indeks 1 czyli tam, gdzie masz po 10. Powinieneś zadeklarować zgodnie z sugestią Kolegi krru czyli:

    Code: c
    Log in, to see the code

    Strukturkę już mamy zdefiniowaną, idziemy dalej.
    Dotyczy drugiej części zadania.

    i tak:
    1) - musisz zadeklarować zmienną typu int, w której umieszczony zostanie rozmiar tablicy wskaźników do struktury typu "struct dane" np:
    Code: c
    Log in, to see the code

    2) - musisz zadeklarować wskaźnik do tablicy elementów typu "wskaźnik do elementów typu struct dane" w nastepujący sposób:
    Code: c
    Log in, to see the code

    3) - w zmiennej LICZNIK określ ilość elementów przydzielanej tablicy np funkcją scanf
    4) - funkcją malloc przydziel pamięć tejże tablicy:

    Code: c
    Log in, to see the code
    5) - wyzeruj za pośrednictwem pętli pola uzyskanej tablicy np:
    Code: c
    Log in, to see the code

    - Pamiętaj, że funkcja malloc(), w przeciwieństwie do calloc(), nie zeruje przydzielonej pamięci więc musisz zrobić to sam!
    - Pamiętaj, że mamy do czynienia ze WSKAŹNIKIEM DO TABLICY elementów typu... a nie TABLICY elementów typu... więc do pól tak zadeklarowanej tablicy należy odwoływać się w sposób:
    (*MOJA_TABLICA)[a]
    a nie:
    MOJA_TABLICA[a]

    No to już z górki:
    Tworzysz funkcją malloc kolejne rekordy i przypisujesz je poszczególnym elementom tablicy (*MOJA_TABLICA) np:
    Code: c
    Log in, to see the code

    wypełniasz je itd.
    Jeśli usuwasz rekord z tablicy, to po prostu użyjesz funkcji free a nastepnie elementowi tablicy odpowiadającemu usuniętej strukturze nadajesz wartość NULL. Rozmiar tablicy będziesz miał zachowany w zmiennej LICZNIK.
    P.S.
    Pilnuj, aby przydzielona pamięć, kiedy już będzie niepotrzebna, została zwolniona (funkcja free() ). Zapobiegnie to tak zwanym "wyciekom pamięci". Wiem, teraz jest tyle RAMu, że... Ale mimo wszystko, świadczy to o programiście!
  • Helpful post
    #5
    winuser2
    Level 17  
    @up

    Wszystko dobrze, tylko wytłumacz mi po co deklarujesz to w ten sposób :

    Code: c
    Log in, to see the code


    Quote:
    musisz zadeklarować wskaźnik do tablicy elementów typu "wskaźnik do elementów typu struct dane" w nastepujący sposób:


    Po co ? Przecież on potrzebuje zwykłą tablicę, czyli jeden wskaźnik na obszar pamięci zaalokowany przez menadżer sterty. Natomiast to co ty robisz, to wskaźnik na pierwszy element tablicy wskaźników.

    Wszystko zawarłem w pierwszej odpowiedzi. Koledzy chyba posto-nabijacze ?

    Code: c
    Log in, to see the code


    Quote:
    5) - wyzeruj za pośrednictwem pętli pola uzyskanej tablicy np:

    Code: c
    Log in, to see the code


    Zamiast tej pętli i zaciemniania kodu dodatkowymi zmiennymi wystarczy użyć memset jak już.

    EDIT:


    Quote:
    Hmm... Na początku to trochę rozwlekłeś strukturę. Niepotrzebnie pola: imię, nazwisko, dzień, miesiąc, rok zdefiniowałeś jako tablice dwuwymiarowe a konkretnie niepotrzebnie dodałeś indeks 1 czyli tam, gdzie masz po 10. Powinieneś zadeklarować zgodnie z sugestią Kolegi krru czyli:


    To prawda, umknęło mi.
  • Helpful post
    #6
    krru
    Level 33  
    winuser2 wrote:

    Quote:
    musisz zadeklarować wskaźnik do tablicy elementów typu "wskaźnik do elementów typu struct dane" w nastepujący sposób:


    Po co ? Przecież on potrzebuje zwykłą tablicę, czyli jeden wskaźnik na obszar pamięci zaalokowany przez menadżer sterty. Natomiast to co ty robisz, to wskaźnik na pierwszy element tablicy wskaźników.


    Fakt, w podanym kodzie jest trochę za duzo wskaźników.
    W treści zadania jest podane jasno - w wersji ze wskaźnikami ma być statyczna tablica stu wskaźników i każdy rekord oddzielnie allokowany, a nie jeden blok na wszystkie dane.

    Quote:
    Napisać program analogiczny do pkt 6.3 z ta różnicą, że dane powinny być przechowywane w tablicy stu elementowej przechowującej wyłącznie adresy do danych, a dane będą alokowane w sposób dynamiczny w miarę potrzeb użytkownika.



    Code: c
    Log in, to see the code


    i gdzieś dalej w kodzie:

    Code: c
    Log in, to see the code


    Szczegóły, takie jak w którym momencie allokować, w którą zmienną wczytywać dane itp można rozpisać na wiele sposobów. Może nie najlepszy w tym momencie, ale w miarę podobny do STL byłby taki, w którym mamy jedną zmienną typu dane, w nią wczytujemy dane od usera i jeśli są gotowe i poprawne allokujemy nowy rekord, kopiujemy dane i zapamiętujemy wskaźnik w tablicy. Coś jak push_back w kontenerach STL.
  • Helpful post
    #7
    Krzysztof Gustaw
    Level 23  
    Witam!
    A'propos "przytyku"
    winuser2 wrote:

    Wszystko zawarłem w pierwszej odpowiedzi. Koledzy chyba posto-nabijacze ?

    A to fragment cytowanej, Twojej pierwszej odpowiedzi:
    winuser2 wrote:
    1. Pobierasz rozmiar tablicy przy użyciu scanf("%d", &rozmiar) zmienna rozmiar jest typu całkowitego.


    Skoro pobiera się rozmiar tablicy przy pomocy funkcji scanf od razu zakładam, że rozmiar tablicy nie jest z góry znany, zatem tablica ma być tworzona w sposób dynamiczny i taki był też tok mojego dalszego postępowania. To chyba taki doświadczony programista rozumie, prawda?

    Deklaracja tablicy w sposób np

    Code: c
    Log in, to see the code
    oznacza, że deklarowana jest stuelementowa tablica wskaźników do struktur typu struct dane. Tak zadeklarowana tablica jest tworzona na etapie kompilacji i jest jej przydzielana odpowiednia ilość pamięci na stosie. Jeśli teraz będziesz chciał zmienić jej rozmiar w sposób np:
    Code: c
    Log in, to see the code
    kompilator wyprodukuje komunikat o błędzie. Spowodowane jest to tym, że nazwa tak zadeklarowanej tablicy jest traktowana jako STAŁA zatem niedozwolone jest nadawanie jej nowej wartości. Identyfikator tak zadeklarowanej tablicy jest traktowany tak, jakby był mu przypisany klasyfikator const. Inaczej sprawa przedstawia się, jeśli zadeklaruje się tablicę w ten sposób:
    Code: c
    Log in, to see the code
    Jest to deklaracja zmiennej typu "wskaźnik do tablicy elementów typu struct dane" o nieznanym z góry rozmiarze. Oczywiście, można nadać rozmiar tejże tablicy, ale to bez sensu :D
    Tak zadeklarowana zmienna jest traktowana jako ZMIENNA typu "wskażnik do tablicy elementów typu..." i można jej przypisywać wartości, co pozwala na dynamiczne operowaniem wielkością tablicy (np za pośrednictwem funkcji z rodziny malloc).
    Powtarzam: Zasugerowałem się: patrz ad 1.
    Ale wyszło na to, że wybiegłem nieco w przyszłość,zatem w moim poprzednim poście znajduje się już odpowiedź na zadanie typu: "A teraz utwórz tablicę typu TYP o nieznanej z góry liczbie elementów (być może z możliwością dynamicznej zmiany jej rozmiaru...) itd"
    winuser2 wrote:

    Kod C - [rozwiń]for (a = 0; a < LICZNIK; (*MOJA_TABLICA)[a ++] = NULL);


    Zamiast tej pętli i zaciemniania kodu dodatkowymi zmiennymi wystarczy użyć memset jak już.

    Sęk w tym, że wartość NULL z reguły przyjmuje wartość "0" (zero) ale standard języka "C" nie mówi, że tak jest w istocie. NULL może być dowolną wartością, niekoniecznie zerową, taką która gwarantuje, że ten adres jest traktowany jako "pusty". Tak więc memset niekoniecznie może załatwić sprawę... Jeśli już, należy wyedytować plik stdio.h i sprawdzić jaka wartość odpowiada NULL

    P.S.
    do winuser2: przepraszam, jeśli uraziłem...
    I do wszystkich obserwujących ten temat:
    Chodzi mi o to, żeby Kolega dtoretto14 przyjrzał się bacznie wskaźnikom.Gdy opanuje wskaźniki, wtedy funkcje z rodziny malloc() również opanuje. Jak zapewne zdajecie sobie sprawę, wskaźniki wraz z instrukcją "goto" cudownie zaciemniają program i są powodem frustracji niedoświadczonych programistów. Zatem uznałem, że w Jego interesie jest opanowanie wskaźników i operacji na nich, ponieważ w tym właśnie tkwi siła języka "C". Poza tym, wskaźniki i tablice mają ze sobą tak ścisły związek, że w porządnych podręcznikach są omawiane razem.
    Teraz zaczną się pewnie wskaźniki do funkcji... :D
  • #8
    dtoretto14
    Level 10  
    Dzięki wszystkim za pomoc. Faktycznie muszę dokładnie przeczytać zasadę używania wskaźników i operowania nimi. Mój problem właśnie polega na tym, że na początku nauczyłem się pisać skrypty w php i niektóre funkcje tj. pętle for mają identyczną składnie tak jak w c, natomiast większość nie i niekiedy łatwo mi załapać co i jak, lecz częściej mam problemy, na dodatek jakość kształcenia na polibudzie nie jest zadowalająca.