cdata-structuresstackdot-operator

Why isn't this code working? It works when I use dot operator, instead of ->operator and pointers


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

struct stack {
    int size;
    int top;
    int *arr; 
};

int isEmpty(struct stack* ptr) {
    if (ptr->top == -1) {
        return 1;
    }
    else {
        return 0;
    } 
}

int isFull(struct stack * ptr) {
    if (ptr->top == ptr->size - 1) {
        return 1;
    }
    else {
        return 0;
    } 
}

int main() {
    // struct stack s;
    // s.size = 80;
    // s.top = -1;
    // s.arr = new int[s.size];        // dynamically allocating memory of s.size integers from s.arr pointer
    
    struct stack * s;           // using pointer so we can send s and its instances to functions as well
    s->size = 80;
    s->top = -1;
    s->arr = (int*)malloc(s->size * sizeof(int));      // same stuff, different way
    
    // manually pushing a value into the stack s
    s->arr[0] = 7;
    s->top++;
  
    // checking if the stack is empty
    if (isEmpty(s)) {
        printf("The stack is empty.");
    }
    else {
        printf("The stack is not empty.");
    }
  
    // checking if the stack is full
    if (isFull(s)) { 
        printf("The stack is full.");
    }
    else {
        printf("The stack is not full.");
    }

    return 0; 
}

Solution

  • The definition struct stack * s; is an uninitialized pointer. You follow this definition immediately by assignments to the memory it points to, which can literally be anywhere. As a result it's undefined behavior. It's very likely your program will crash, but other strange things can happen too. It might even seem to work sometimes.

    Much like you're allocating memory for your array with malloc, you need to allocate memory for the struct:

    struct stack *s = malloc(sizeof(struct stack));
    

    If you don't need dynamic memory, you can just point the pointer at wherever in memory you stored the struct. The point is that you must point it to valid memory.

    Now, I think that you've misunderstood something fundamental here, based on your comment:

    // using pointer so we can send s and its instances to functions as well

    This is not necessary. See below:

    struct stack s;
    s.size = 80;
    s.top = -1;
    s.arr = malloc(s->size * sizeof(int));
    
    // push a value onto the stack
    s->arr[0] = 7;
    s->top++;
    
    // check if the stack is empty
    if (isEmpty(&s)) {
        printf("The stack is empty.\n");
    }
    

    Notice here I declared your struct on the stack (referring to memory area and not to be confused with your struct that is also called "stack"). To get a pointer to that, you use &s.

    So with this, you can also define a function that pushes values:

    int push(struct stack *s, int value) {
        if (isFull(s))
            return 0;
        s->top++;
        s->arr[s->top] = value;
        return 1;
    }
    

    And you invoke that as:

    push(&s, 7);
    

    I personally do not agree with the use of -1 to represent empty. Notice how you have to do awkward math to determine if the stack is full. You may be better off defining top as the next available index, meaning when it's empty the value is zero, and when it's full the value is size.