0

I wanted to create a dynamic stack just for learning purposes, but I ran into a problem: I'd like to push() function be able to handle if the dstack isn't initialized, and it works when I go through the code in gdb, but when I call the print_dstack() after returning from push() function, the pointer I give to it is NULL (but I initialized *stack and returned the pointer explicitly), so I'm getting stuck on SEGFAULT at this point, because I am trying to read a pointer to NULL. I wonder why does that even happen?

Here's my code:

dstack.h:

#pragma once
#ifndef _DSTACK_H_
#define _DSTACK_H_

enum stack_inits {
  DEFAULT_SIZE = 16,
  DEFAULT_SPACE = 32
};

typedef struct {
    int size;
    int length;
    int *stack;
} dstack_t;

static void *init(const int size);

static void resize(dstack_t *stack);

void *push(dstack_t *stack, int item);

void print_dstack(dstack_t const *stack);

void free_dstack(dstack_t *stack);

#endif

dstack.c:

#include "dstack.h"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>

static void *init(const int size)
{
  dstack_t *stack;
  if( (stack = (dstack_t*)malloc(sizeof(dstack_t))) == NULL)
    return NULL;
  if( (stack->stack = (int*)malloc(size * sizeof(int))) == NULL) {
    free(stack->stack);
    free(stack);
    return NULL;
  }
  stack->size = size;
  stack->length = 0;

  return stack;
}

static void resize(dstack_t *stack)
{
  stack->size += DEFAULT_SPACE;
  int *newStack = realloc(stack->stack, sizeof(int) * stack->size);
  assert(newStack);
  stack->stack = newStack;
}

void *push(dstack_t *stack, int item)
{
  if(stack == NULL)
    stack = init(DEFAULT_SIZE);
  if(stack->stack == NULL)
    if( (stack->stack = (int*)malloc(DEFAULT_SIZE * sizeof(int))) == NULL) {
      free(stack->stack);
      return NULL;
    }
  if(stack->length == stack->size)
    resize(stack);
  stack->stack[stack->length++] = item;
  return &stack->stack[stack->length-1];
}

void print_dstack(dstack_t const *stack)
{
  assert(stack);
  for(int i = 0; i < stack->length; i++)
  {
    if(i % 2 == 0)
      putchar('\0');
    printf("stack[%d] = %d\t", i, stack->stack[i]);
  }
}

void free_dstack(dstack_t *stack)
{
  free(stack->stack);
  free(stack);
}

stack_test.c:

#include "dstack.h"
#include <stdio.h>

int main()
{
  dstack_t *stack = NULL;
  push(stack, 10);

  print_dstack(stack);
  free_dstack(stack);
  return 0;
}
0

1 Answer 1

0

You modify push's stack, but not the caller's argument (main's stack), to which push doesn't have access. If you want push to modify the caller's pointer variable, you will need to pass a pointer to it as follows:

dstack_t * stack = NULL;
push( &stack, 10 );

You could also use something like the the following:

dstack_t * stack = NULL;
stack = push( stack, 10 );

Adjust push accordingly.

Personally, I think it's appropriate for push to require an initialized stack.

Sign up to request clarification or add additional context in comments.

3 Comments

But isn't dstack_t *stack the address already? Also, both Clang and GCC throw the warning of incompatible types, because (&stack) == (**stack) in this case.
No, the value of stack isn't the address of stack, it's NULL or the address of the block of memory you initialize in init. Neither of those are the address push would need. As I said in my answer, push needs the address of the variable in order to change it.
Re "both Clang and GCC throw the warning of incompatible types", You didn't make the necessary changes to push to accommodate the change in argument. Basically, every instance of stack needs to be replaced with *stack (including in the parameter list, which results in dstack_t **stack).

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.