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

C/C++ - Funkcja zwracająca na wskaźnik na strukturę z dynamiczną alokacją pamięci

Object Storage Arubacloud
0 głosów
1,055 wizyt
pytanie zadane 10 kwietnia 2018 w C i C++ przez GoRo3 Obywatel (1,640 p.)

Cześć Wam!

Mam taki problem. Piszę sobie implementację stosu w ANSI-C. Stos jest oparty na strukturze z tablicą 10'cio elementową intów. Gdybym to robił w C++ to bym skorzystał z dobrodziejstwa klasy ale nie robię tego tak, muszę przygotować to w C. Do tej pory mam coś takiego: 

// Definicja struktury. 

typedef struct 
{

    int tab[10];
    int sptr;

    int (*top)(void *s);
    void (*push)(void *s, int dane);
    void (*pop)(void *s);
    
}STOS;

int top(STOS *s)
{
    if (s->sptr)
        return s->tab[s->sptr - 1];
    return -1;
}
void push(STOS *s, int dane)
{
    if (s->sptr < 10)
        s->tab[s->sptr++] = dane;
}
void pop(STOS *s)
{
    if (s->sptr)
        s->sptr--;
}

Czyli klasyczna struktura z wskaźnikami na funkcję - prekursor klasy. 

Teraz pomyślałem aby zainicjować ją w taki sposób: 

STOS init(void)
{
    STOS *temp = (STOS*)malloc(sizeof(STOS));
    
    temp->sptr = 0;
    temp->top = (void*)top;
    temp->push = (void*)push;
    temp->pop = (void*)pop;

    return *temp;
}

Czyli funkcją w której inicjuje strukturę łącznie z dynamiczną alokacją pamięci, natomiast sama funkcja zwraca wskaźnik na utworzoną strukturę. 

W mainie inicjuje sobie obiekt na którym będę pracował w następujący sposób: 

STOS obiekt = init(); // inicjalizacja obiektu struktury stos "obiekt".

No i teraz mam kilka pytań bo kod ogólnie działa ale: 

1. NIe wiem jak zrobić coś na styl destruktora. Czy w funkcji "free()" powinienem niszczyć obiekt *temp czy "obiekt"? tak aby nie powstawały wycieki pamięci. I ewentualnie jak to zaimplementować? 

2. Alokując dynamicznie pamięć dla struktury każę kompilatorowi przez funkcję malloc zarezerwować przestrzeń wielkości struktury, ale czy w takim razie zmienne w strukturze będą alokowane na stercie? Czy one są na stosie? i powinienem jeszcze zaalokować dynamicznie tablicę tab[10].

 

Z Góry dziękuję za odpowiedź! 

1 odpowiedź

+1 głos
odpowiedź 10 kwietnia 2018 przez Qwerty96 Stary wyjadacz (13,580 p.)
wybrane 10 kwietnia 2018 przez GoRo3
 
Najlepsza

1, W tej strukturze nie ma nic, co potrzebowałoby destruktora.

2. Ta funkcja init jest źle napisana.

Czyli funkcją w której inicjuje strukturę łącznie z dynamiczną alokacją pamięci, natomiast sama funkcja zwraca wskaźnik na utworzoną strukturę. 

No właśnie nie zwraca wskaźnika. Zwraca zaalokowaną strukturę przez wartość powodując wyciek pamięci.

komentarz 10 kwietnia 2018 przez GoRo3 Obywatel (1,640 p.)

OK, czyli tak nie tworzymy inicjalizacji? 

Przed chwilą zmodyfikowałem funkcję na coś takiego: 

STOS *init(void)
{
    STOS *temp = (STOS*)malloc(sizeof(STOS));
    
    temp->sptr = 0;
    temp->top = (void*)top;
    temp->push = (void*)push;
    temp->pop = (void*)pop;

    return temp;
}

A w mainie tworze sobie wskaźnik na strukturę o tak: 

STOS *obiekt = init(); 

I zauważyłem, że mogę teraz wywołać funkcje free(obiekt) na końcu kodu. Czy teraz nie będzie wycieku?

Ten stos będę zaraz pakował w listę takich obiektów więc zależy mi na tym aby przy dodawaniu i usuwaniu kolejnego nie mieć wycieków pamięci.  

1
komentarz 10 kwietnia 2018 przez j23 Mędrzec (194,920 p.)
edycja 11 kwietnia 2018 przez j23

Nie lepiej tak, bez rzutowania:

typedef struct STOS_tag
{
 	int tab[10];
	int sptr;
 
	int (*top)(struct STOS_tag*);
	void (*push)(struct STOS_tag*, int);
	void (*pop)(struct STOS_tag*);
	 
} STOS;
 
...

STOS* init(void)
{
	STOS *temp = (STOS*)malloc(sizeof(STOS));

	temp->sptr = 0;
	temp->top = top;
	temp->push = push;
	temp->pop = pop;
	return temp;
}

???

 

Czy teraz nie będzie wycieku?

Nie.

komentarz 10 kwietnia 2018 przez GoRo3 Obywatel (1,640 p.)
A no widzisz :) Mądrego zawsze warto posłuchać(poczytać). Dzięki, czegoś się nowego nauczyłem.
1
komentarz 11 kwietnia 2018 przez mokrowski Mędrzec (155,460 p.)

@GoRo3, zastanów się czy potrzebujesz takiej mimikry obiektu do tego co znasz z C++. W C często po prostu tworzy się funkcje wolne które przyjmują strukturę obiektu. Takie "upychanie wskaźników funkcji w strukturze", niewiele więcej daje oprócz "wołam przez kropkę". Mogło by się bronić gdybyś hermetyzował widoczność funkcji w łączeniu zewnętrznym poprzez static. Ale tu tego nie masz albo nie wiedziałeś że możesz zrobić.

Dodatkowo z dowolnego miejsca mogę przestawić funkcję na swoją własną bo wskaźniki nie są const.

Zastanów się także dlaczego i czy zrobiłeś to świadomie, rozdzieliłeś pop() od top(). U Ciebie struktura danych stosu nie jest dynamiczna.

Jak chcesz zmierzyć się z tematem OOP w C (bez plusów), poszukaj pliku: ooc.pdf . Klasyczna i dość już stara pozycja ale pokazuje co można zrobić "w temacie".

1
komentarz 16 kwietnia 2018 przez GoRo3 Obywatel (1,640 p.)

Cześć 

Dzięki za podpowiedzi. Co do hermetyzacji oraz przełączania wskaźników to wiedziałem, chociaż o tym nie pomyślałem.  Problem w tym, że musiałem napisać specyficzną strukturę danych - listę dynamiczną na którą składa się stos oraz sterta(jako drzewo binarne / kopiec) jako zadanie na zajęciach a miałem na to tylko bardzo ograniczony czas. 

Były ściśle określone założenia tak jak to, że stos ma przyjąć tylko 10 liczb więc nie było potrzeby robienia tego dynamicznie, wystarczyła tablica intów. A ponieważ chciałem się czegoś nauczyć to stos rzeźbiłem według własnej wyobraźni a nie gotowców, który jest sporo na necie. 

Jak chcesz to mam na gitlabie gotowy projekt, możesz rzucić sobie okiem: LINK

Teraz jak mam gotową wersję beta to będę sobie rzeźbił właśnie szczególiki. 

2
komentarz 16 kwietnia 2018 przez mokrowski Mędrzec (155,460 p.)
edycja 16 kwietnia 2018 przez mokrowski

Ok, nie byłem precyzyjny. Konieczność rozdzielania pop() i top() wynika z tego że kontenery w C++ alokują dane na stercie. W trakcie odczytu może dojść do problemem z pamięcią stąd ten podział. Jeśli nie masz dynamicznej struktury, wystarczy samo pop() które wykona i zwrócenie wartości i zdjęcie.

Już zerkam na projekt.

Jakieś małe uwagi mogły by się znaleźć ale to nic ważkiego.

https://stackoverflow.com/questions/605845/do-i-cast-the-result-of-malloc?utm_medium=organic&utm_source=google_rich_qa&utm_campaign=google_rich_qa

https://wiki.sei.cmu.edu/confluence/pages/viewpage.action?pageId=87152177

C potrafi łączyć string'i zamiast pisać tak:

printf("-------------------------ID-----------------------\n");
                printf("\t\t MENU WYBORU KOMPUTERA:\n");

Możesz zapisać tak:

printf("-------------------------ID-----------------------\n"
         "\t\t MENU WYBORU KOMPUTERA:\n"
         "i następna linia... \n"
         "i jeszcze jenda... \n");

Liczniki raczej obsługuj danymi unsigned. Operacje przepełnienia na unsigned są determinowane zapisami standardu. Dane ze znakiem maja po przepełnieniu niezdefiniowane zachowanie.

Zamiast exit(0), stosuj raczej exit(EXIT_SUCCESS); z <stdlib.h> i exit(EXIT_FAILURE) jeśli jest jakiś problem.

W makefile cel clean powinien być .PHONY

Raczej chyba w tym projekcie lista "jest służebna" dla stosu i sterty. Lepiej było by to listę ukryć w wydzielonym katalogu lub wszystkie 3 umieścić w dedykowanych. Tu się można spierać ale możesz w większych projektach wygenerować sobie cykliczne zależności a to może być toksyczne. Tu nie ma o czym rozmawiać bo to mikro-projekt.

Wiele standardów branżowych co do kodu w C, wymaga otaczania operacji logicznych i porównań nawiasami. Ze względu na nieregularną kolejność wiązania operacji. Radzę to zrobić.

Nawet jeśli jest tylko jedna linia w if'ie, rób nawiasy klamrowe. Jeśli w przyszłości ktoś będzie modyfikował kod, nie popełni błędu umieszczenia instrukcji poza ciałem zakresu.

To będzie dość irytujące w użyciu stosowanie w drzewie w niektórych funkcjach pojedynczego wskaźnika a w innych podwójnego.

 W przypadku komend diagnostycznych, raczej drukuj na stderr z pomocą fprintf(..) a nie na stdout.

Tyle na pierwszy rzut oka... kodu nie uruchamiałem.. 

 

komentarz 17 kwietnia 2018 przez GoRo3 Obywatel (1,640 p.)
Panie Kolego,

Szkoda że nie mogę Tobie jakiś punktów dać ale napewno piwko przy jakimś spotkaniu postawię ;)

Dziękuję za rady. Na pewno wykorzystam.
komentarz 17 kwietnia 2018 przez j23 Mędrzec (194,920 p.)
W lewym górnym rogu masz trójkąt - nim oddajesz głos na komentarz.
komentarz 17 kwietnia 2018 przez mokrowski Mędrzec (155,460 p.)

@GoRo3, napewno piwko przy jakimś spotkaniu postawię ;) 

Oj bo wezmę poważnie i doprowadzę do egzekucji :) Kliknij strzałkę w górę i będzie ok.

Podobne pytania

0 głosów
1 odpowiedź 279 wizyt
pytanie zadane 7 kwietnia 2023 w C i C++ przez Zuzan Początkujący (390 p.)
0 głosów
1 odpowiedź 567 wizyt
pytanie zadane 19 listopada 2018 w C i C++ przez Roman1212 Początkujący (460 p.)
0 głosów
1 odpowiedź 222 wizyt

92,568 zapytań

141,421 odpowiedzi

319,627 komentarzy

61,956 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

Kolejna edycja największej imprezy hakerskiej w Polsce, czyli Mega Sekurak Hacking Party odbędzie się już 20 maja 2024r. Z tej okazji mamy dla Was kod: pasjamshp - jeżeli wpiszecie go w koszyku, to wówczas otrzymacie 40% zniżki na bilet w wersji standard!

Więcej informacji na temat imprezy 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!

...