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

Ok odpowiedź jest taka (tłumacznie):

Ta funkcja jest w 100% poprawna

W języku C int foo() { oznacza: zdefiniuj funckję foo zwracającą wartosć typu int oraz pobierającą niezdefiniowaną liczbę parametrów.

"Moja" dezorientacja wynika, z tego, że w C oraz C++ int foo(void) oraz int foo() oznaczają dokładnie to samo.

W C by zdefiniować funkcję bezparamerową musi być ona zdefiniowana jako:

int foo(void) {

Przekładając to na twój kod, dodanie void od razu oczyszcza sytuację: https://godbolt.org/z/sMhK7GT3K i właściwy błąd jest raportowany.

0

@MarekR22: Jeśli komunikat po dodaniu voida wyświetla prawidłowy error, to w inicjuj w mainie nie mogę wstawić żadnego argumentu, a bez tego wyskakuje error o niezainicjowanym stosie, naprawdę nie wiem co mam z tym zrobić.

Tym bardziej jestem zmieszany bo taki kod z taka sama funkcja inicjuj działa perfekcyjnie:

#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 main()
{
    t_stos *stos;
    inicjuj(&stos);
    int a1;
    int b1;
    printf("Podaj pierwszy argument:\n");
    scanf("%d", &a1);
    printf("Podaj drugi argument:\n");
    scanf("%d", &b1);
    push(stos, a1);
    push(stos, b1);
    int a;
    int b;
    a=pop(stos);
    b=pop(stos);
    printf("%d", a+b);
}
0

To dlatego, że masz złe nazwy w kodzie, przez co nie ogarniasz co do czego służy.

t_elem * inicjuj() {
    return NULL;
}

takie coś wygląda na funkcję inicjującą wskaźnik na t_elem ! To nie wykonuje jakiejkolwiek operacji na t_stos lub wskaźniku na t_stos.

Po prostu nazwa twojej funkcji jest zbyt ogólna przez co może być użyte w jakim kontekście.

Ja bym poprawił to przez dodanie prefiksów do funkcji, które podpowiadają, na czym operuje dana funkcja. Wtedy będzie ci łatwiej ogarnąć ten kod.
To jest doskonały przykład jak bardzo jest ważne właściwe nadawanie nazw symboli.

0

@MarekR22: Czyli sugerujesz żeby zmienić t_elem na cokolwiek innego?

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