0

I have here a function get_stack that instantiates a stack and should return a pointer to the stack, but doesn't. Still, the program continues and updates the stack correctly.

#include <stdio.h>
#include <stdlib.h>    
#define STACKLIM 1000


struct stack {
    char data[STACKLIM];
    int top;
};

typedef struct stack* Stack;

Stack get_stack() {
    Stack s = (Stack)(malloc(sizeof(struct stack)));
    s->top = -1;
    //return s;
}

void push(Stack s, char val) {
    if(s->top == STACKLIM) {
        printf("ERROR: Stack Overflow\n");
    }
    else {
        s->top += 1;
        s->data[s->top] = val;
    }
}

void display(Stack s) {
    int i;
    printf("Stack -> ");
    for(i = 0; i <= s->top; i++) {
        printf("%c ", s->data[i]);
    }
    printf("\n");
}

int main() {
    Stack d = NULL;
    d = get_stack();
    push(d, 'a');
    display(d);        
    return 0;
}

It's like the return statement does not matter. What could be the reason for this? I am using gcc 4.5.2 on an RH5 machine.

Unos
  • 1,293
  • 2
  • 14
  • 35
  • 2
    What happens when you compile with `-Wall`? Also, look at this link: http://stackoverflow.com/questions/10079089/implicit-int-return-value-of-c-function – AntonH Sep 05 '14 at 03:47
  • Are you using any optimization flags? Remember that undefined behaviour includes having your program misteriously work. – hugomg Sep 05 '14 at 03:50
  • when i compile with `-Wall`, I do get `warning: control reaches end of non-void function`, but program still runs without any problems. – Unos Sep 05 '14 at 03:53
  • Remember, you have defined `Stack get_stack()`, so `get_stack()` is declared as a function returning type `struct stack *` -- you are just missing the return statement in the definition. I'm with hugomg and AntonH. You have been blessed by `UB`. – David C. Rankin Sep 05 '14 at 03:55
  • This is an entirely normal accident. By convention, the return value of a function is passed back through a CPU register. Like EAX for an Intel processor. That register is quite likely to contain the pointer by accident, especially when you don't turn on the optimizer. – Hans Passant Sep 05 '14 at 04:01
  • I agree with all of you now. When i compile it with `gcc -Wall -O3` and run, it crashes. So lesson learnt is don't depend on undefined behavior... – Unos Sep 05 '14 at 04:09

2 Answers2

4

Whether or not the compiler diagnoses it, it's always an error to fall off the end of a function (i.e. reach the closing }) without returning a value if the function has a non-void return type. It's Undefined Behavior.

Now, Undefined Behavior can manifest itself in many different ways. It can result in your program crashing, receiving the wrong value, erasing your hard drive, sending a nasty email to your mother, or appearing to work successfully with no other ill effects. All of these behaviors are permitted by the C language standard. Just because the code may appear to work correctly, that doesn't mean it's actually correct and working.

If you crash when you invoke Undefined Behavior, you should consider yourself lucky, since those are usually easy to diagnose. But if your program silently corrupts memory and then later crashes in an "impossible" situation, you're going to be in for one heck of a difficult debugging job.

Moral of the story: compile your code with a high warning level (e.g. use the -Wall compiler option; possibly also -Wextra and/or -pedantic if you can bear it; -Werror also strongly recommended). Any compiler worth its salt will catch this kind of error, but often times not at the default warning level.

Adam Rosenfield
  • 390,455
  • 97
  • 512
  • 589
  • I seem to remember one compiler that, in one case of undefined behaviour, started a game of `Snake`, or something to that effect. Wish I could find the reference ... – AntonH Sep 05 '14 at 03:59
  • @AntonH: It's not quite Snake, but sufficiently advanced compilers [can cause time travel when they detect undefined behavior](http://blogs.msdn.com/b/oldnewthing/archive/2014/06/27/10537746.aspx) — if they can reason that certain conditions would eventually trigger UB later, they can aggressively optimize away code that would otherwise be ordinarily defined behavior. – Adam Rosenfield Sep 05 '14 at 04:07
1

It was answered before: Function returns value without return statement .

The gist of it: when doing d = get_stack(), the system looks into some place in memory where it expects to find the return value. Whatever trash is in that place in memory - counts as the return.

It may be that "eax register" or a slot in the call stack. You are just lucky that the return from malloc() got stuck in that spot. Add a few extra commands into get_stack() and the miracle will disappear.

Community
  • 1
  • 1
Vythe
  • 11
  • 1