Search code examples
cgccassert

Why does this code behave inconsistently?


I'm trying to implement the generic stack illustrated in lectures 5 and 6 in CS107 - Programming Paradigms (online course at Stanford). The following code, representing an example presented in the lectures, compiles, but doesn't seem to behave consistently since I often get an assertion failure.

I've noticed the behavior in Geany and gcc ($ gcc --version gcc (Debian 6.3.0-18) 6.3.0 20170516), but not on `https://www.tutorialspoint.com/compile_c_online.php' so I wonder if it is caused by something in gcc or a bug I don't seem to see now.

Code:

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

typedef struct {
    void *elems;
    int elemSize;
    int logLen;
    int allocLen;

} stack;

void StackNew(stack *s, int elemSize);
void StackDispose(stack *s);
void StackPush(stack *s, void *elemAddr);
void StackPop(stack *s, void * elemAddr);

void StackNew(stack *s, int elemSize)
{
        assert(s->elemSize > 0);
        s->elemSize = elemSize;
        s->logLen = 0;
        s->allocLen = 4;
        s->elems = malloc(4 * elemSize);
        assert(s->elems != NULL);
}

void StackDispose(stack *s)
{
    free(s->elems);
}

static void StackGrow(stack *s)
{
    s->allocLen *= 2;
    s->elems = realloc(s->elems, s->allocLen * s->elemSize);
}

void StackPush(stack *s, void *elemAddr)
{
    if(s->logLen == s->allocLen)
    StackGrow(s);
    void *target = (char *) s->elems + s->logLen * s->elemSize;
    memcpy(target, elemAddr, s->elemSize);
    s->logLen++;
}

void StackPop(stack *s, void *elemAddr)
{
    void *source = (char *) s->elems +
            (s->logLen - 1) * s->elemSize;
    memcpy(elemAddr, source, s->elemSize);
    s->logLen--;
}


int main(void)
{
  const char *friends[] = {"Al", "Bob", "Carl"};

  stack stringStack;
  StackNew(&stringStack, sizeof(char *));
  int i;
  for (i = 0; i < 3; i++){
      char *copy = strdup(friends[i]);
      StackPush(&stringStack, &copy);
  }

  char *name;

  for (i = 0; i < 3; i++) {
    StackPop(&stringStack, &name);
    printf("%s\n", name);
    free(name);
  }
  StackDispose(&stringStack);
  return 0;

}

Sample output for several consecutive executions without any source modifications:

$ ./stack4
stack4: stack4.c:21: StackNew: Assertion 's->elemSize' > 0 failed.
Aborted
$ ./stack4
stack4: stack4.c:21: StackNew: Assertion 's->elemSize' > 0' failed.
Aborted
$ ./stack4
Carl
Bob
Al
$ ./stack4
Carl
Bob
Al
$ ./stack4
Carl
Bob
Al
$ ./stack4
stack4: stack4.c:21: StackNew: Assertion 's->elemSize' > 0' failed.
Aborted
$ ./stack4
stack4: stack4.c:21: StackNew: Assertion 's->elemSize' > 0 failed.
Aborted
$ ./stack4
Carl
Bob
Al
$ ./stack4
stack4: stack4.c:21: StackNew: Assertion 's->elemSize' > 0' failed.

Solution

  • You're checking s->elemSize before setting it. You're using an uninitialized variable.

    Fix: Either assert(elemSize > 0) (check the function parameter, not the member of s), or do the assert after the s->elemSize = elemSize assignment.