Program received signal SIGSEGV, Segmentation fault.

0

Cześć,

Napisałem program, a bardziej zmodyfikowałem wcześniej napisany program który działał na bazie stosu na bazie tablicy, teraz chciałem żeby działał na bazie stosu na bazie listy jednokierunkowej.
Niestety w debuggerze wywala mi Program received signal SIGSEGV, Segmentation fault. w 45 linii.
Program przestaje działać w momencie próby użycia funkcji push w 134 linii.

Debugger wypluwa dokładnie przy próbie wpisania liczby 1:

Program received signal SIGSEGV, Segmentation fault.
0x0000555555555336 in push (stos=0x0, dana=1) at main.c:45
45 stos->glowa=dodaj_poczatek(stos->glowa, dana);

Może ktoś zasugerować w czym problem? Nie jestem zbyt dobry z programowania i nie rozumiem w czym jest problem.

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


#define MAXOP 100       /* Dopuszczalny rozmiar liczby lub operatora */
#define NUMBER '0'      /* Sygnal mowiacy ze pobrano liczbe */   
#define BUFSIZE 100     /* Dopuszczalny rozmiar bufora */

typedef struct elem {
    int dana;
    struct elem * nast;
} t_elem;

t_elem * inicjuj() {
    return NULL;
}

t_elem * tworz(int dana) {
    t_elem * nowy=malloc(sizeof(t_elem));
    nowy->dana=dana;
    nowy->nast=NULL;
    return nowy;
}

t_elem * dodaj_poczatek(t_elem * poczatek, int dana) {
    t_elem * nowy=tworz(dana);
    nowy->nast=poczatek;
    return nowy;
}

t_elem * usun_poczatek(t_elem * poczatek) {
    if(poczatek == NULL)
        return NULL;
    t_elem * nast=poczatek->nast;
    free(poczatek);
    return nast;
}

typedef struct stos {
    t_elem * glowa;
} t_stos;

void push(t_stos * stos, int dana) {
    stos->glowa=dodaj_poczatek(stos->glowa, dana);
}

int isempty(t_stos * stos) {
    if(stos->glowa == NULL) {
        return 1;
    }
    else {
        return 0;
    }
}

int pop(t_stos * stos) {
    if(isempty(stos))
        return 0;
    int dana = stos->glowa->dana;
    stos->glowa=usun_poczatek(stos->glowa);
    return dana;
}

int getch(void);
void ungetch(int);

/* Funkcja getop pobierajaca nastepny operator lub liczbe */

int getop(char s[])
{
    int i, c;
    
    while ((s[0] = c = getch()) == ' ' || c == '\t');   /* Pominiecie spacji */
    s[1] = '\0';
    if (!isdigit(c) && c != '.')
        return c;   /* c nie jest liczba */
    i = 0;
    if (isdigit(c)) /* Pobierz czesc calkowita */
        while (isdigit(s[++i] = c = getch()));
    if (c == '.') /* Pobierz czesc ulamkowa */
        while (isdigit(s[++i] = c = getch()));
    s[i] = '\0';
    if (c != EOF)
        ungetch(c);
    return NUMBER;
}

char buf[BUFSIZE]; /* Bufor dla ungetch */
int bufp = 0;   /* Nastepna wolna pozycja w buforze */

int getch(void) /* Pobiera znak */
{
    return (bufp > 0) ? buf[--bufp] : getchar();
}

void ungetch(int c)  /* Wycofuje znak do strumienia danych wejsciowych */
{
    if (bufp >= BUFSIZE)
    printf("Blad: zbyt wiele znakow.\n");
    else
        buf[bufp++] = c;
}


int main()
{
    t_stos * stos;
    inicjuj(&stos);
    printf("Kalkulator RPN\n");
    printf("\n");
    printf(" + -> dodawanie\n");
    printf(" - -> odejmowanie\n");
    printf(" * -> mnozenie\n");
    printf(" / -> dzielenie\n");
    printf(" = -> wyswietlenie wyniku badz gornego elementu stosu\n");
    printf(" e -> sprawdzenie czy stos jest pusty \n");
    printf(" p -> wyjecie 'gornego' elementu stosu, wyswietlenie go i jego usuniecie\n");
    printf(" q -> zakonczenie dzialania kalkulatora\n");
    printf("\n");
    printf("Podaj dzialanie:\n");
    
    int type;
    int op2;             /* Wprowadzenie dwoch       */
    int op3;             /* pomocniczych zmiennych   */
    char s[MAXOP];
    
    while ((type =getop(s)) != EOF)
    {
       
        switch (type)
        {
            case NUMBER:
                push(stos, atof(s));
                break;
            
            case '+':  
                push(stos, pop(stos) + pop (stos));
                break;
                
            case '*':
                push (stos, pop(stos) * pop(stos));
                break;
                
            case '-':
                op2 = pop(stos);
                push(stos, pop(stos) - op2);
                break;
                
            case '/':
                op2 = pop(stos);
                
                if(op2 != 0.0)
                    push(stos, pop(stos) / op2);
                    
                else
                    printf("Blad: proba dzielenia przez 0.\n");
                break;
                
            case '=':
                op3 = pop(stos);
                printf("%d", op3);
                push(stos, op3);
                break;
                
            case 'e':
                op3 = pop(stos);
                if(op3 == 0.0)
                {
                    printf("Stos jest pusty.\n");
                }
                else
                {
                    printf("Stos nie jest pusty.\n");
                }
                push(stos, op3);
                break;
                
            case 'q':
                exit(0);
                break;
            
            case 'p':
                printf("%d", pop(stos));
                break;
                
            case '\n':
            break;
                
            default:
                printf("Blad: nieznana operacja.\n");
                break;
                
        }
    }
    return 0;
}
1
hajgor napisał(a):

t_elem * inicjuj() {
return NULL;
}

Inicjalizujesz NULLem, stąd problem. Po tej inicjalizacji, próbujesz wykonać:

void push(t_stos * stos, int dana) {
stos->glowa=dodaj_poczatek(stos->glowa, dana);
}

na NULLu, tylko, że NULL->głowa nie istnieje.

1

Ja szczerze pisząc nie wiem jak Ci się to kompiluje, skoro inicjuj() nie pobiera żadnego argumentu a Ty przekazujesz mu wskaźnik. Jeśli to jakiś chochlik przy kopiowaniu to pewnie jest to to, co pisał @GutekSan czyli dereferencja null pointera.

0

@GutekSan: Aaa okej, a mógłbym prosić o podpowiedź czym zastąpić tego NULLa? Inicjowałem NULLem bo tak tez pokazał wykładowca na zajęciach więc myślałem ze to będzie działać

1

@hajgor: musisz po prostu utworzyć nowy obiekt. Masz do tego funkcję t_elem * tworz(int dana) .

0

@GutekSan: Nie rozumiem jak mam to zrobić, czy mógłbym prosić o przykład?

0
  1. Przykłady wywołań funkcji w C
    link
  2. Co do przykładu to widzę "Kalkulator RPN" klepnij w google są przykłady
  3. To zadanie ma na celu czegos cię nauczyć koledzy podali już wyżej nawet nazwę funkcji.
1

Kompilator z włączonymi opcjami -O3 -Wall -Wextra -Wpedantic
Wykrywa problem w fazie kompilacji.
Na dodatek address sanitizer mówi co się dzieje:
https://godbolt.org/z/j48aaf8YE

W twoim kodzie nie ma czegoś takiego, co pasuje do: inicjuj(&stos);.
Na dodatek w C nie ma przeciążeń funkcji, więc jeszcze to ci mąci:

t_elem * inicjuj() {
    return NULL;
}

C ma taką durną funkcjonalność domyślnego zakładania istnienia funkcji (implicit function declaration)- to jest powód, że to się kompiluje a nie powinno.
Dziwne jest, że istnienie t_elem * inicjuj() ucisza warning i nie powoduję błędu kompilacji.

Kompilacja jako C++ ładnie raportuje, że coś nie tak jet z tą funkcją: https://godbolt.org/z/vE6v17f36

0

Przede wszystkim, napraw to inicjuj(), żeby pasowało do wywołania, np:

void inicjuj(t_stos* stos) {
stos->glowa = NULL; 
}

a sam stos twórz jako obiekt statyczny:

    t_stos stos;

a nie jako wskaźnik, jak to obecnie robisz:

Dalej, przyjrzałem się bliżej i może użycie metody tworz do inicjalizacji nie będzie właściwe, bo nie mamy tu jeszcze wlasciwej danej, a na stosie powinny być tylko dane pobrane ze strumienia.

Ja bym chyba zrezygnował z osobnej funkcji tworz, a obiekty tworzył wewnątrz dodaj_poczatek, bo wewnatrz tworz ustawiasz:

nowy->nast=NULL

a potem i tak to nadpisujesz własciwą wartością, czyli to pierwsze jest zbędne.

Zrób to tak:

t_elem * dodaj_poczatek(t_elem * poczatek, int dana) {
    t_elem * nowy=malloc(sizeof(t_elem));
    nowy->dana=dana;
    nowy->nast=poczatek;
    return nowy;
}

w ten dodaj_poczatek jest analogiczne w działaniu do usun_poczatek - w jednym masz alokacje pamieci - malloc, w drugim zwalnianie - free.

1

To, że twój kod się kompiluje, jest dla mnie na tyle dziwne i nie do końca zrozumiałe, że założyłem pytanie na SO ze zredukowanym przykładem: https://stackoverflow.com/q/70741364/1387438

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