1

I have encountered a weird malloc behaviour and was hopping someone can shed some light on it.

Here is one function:

struct flowNetwork * createGraph(){
    struct flowNetwork * fN = initFlowNetwork();
    insertAdjMatrix(fN->adjMatrix, 0, 3, 0, 10);
    insertAdjMatrix(fN->adjMatrix, 0, 2, 0, 12);
    insertAdjMatrix(fN->adjMatrix, 0, 1, 0, 5);
    insertAdjMatrix(fN->adjMatrix, 1, 4, 0, 6);
    insertAdjMatrix(fN->adjMatrix, 2, 5, 0, 11);
    insertAdjMatrix(fN->adjMatrix, 4, 5, 0, 5);
    insertAdjMatrix(fN->adjMatrix, 3, 5, 0, 5);
    insertAdjMatrix(fN->adjMatrix, 3, 7, 0, 5);
    insertAdjMatrix(fN->adjMatrix, 4, 5, 0, 5);
    insertAdjMatrix(fN->adjMatrix, 5, 7, 0, 10);
    insertAdjMatrix(fN->adjMatrix, 5, 6, 0, 8);
    insertAdjMatrix(fN->adjMatrix, 7, 8, 0, 16);
    insertAdjMatrix(fN->adjMatrix, 6, 8, 0, 9);
    return fN;
}

Notice the second line calls a function which will return a pointer to a flowNetwork struct. Here is the code for the fuction:

struct flowNetwork *  initFlowNetwork(){
     struct flowNetwork * N = (struct flowNetwork *)malloc(sizeof(struct flowNetwork));
     N->adjMatrix = initAdjMatrix();
     int i;
     for (i = 0; i < NODES; i++)
     {
        N->visitedNodes[i] = 0;
        N->parent[i] = -1;
     }
}

Notice that I never returned a pointer (I originally forgot to add it and noticed this later). Despite not having a return the code work perfectly as if I did have a return pointer statement.
Does anyone know why this works?

user3014911
  • 169
  • 1
  • 1
  • 6
  • How are you compiling this? – tijko Mar 31 '15 at 22:55
  • Side note: I think we are supposed to check the results of `malloc` to make sure it succeeded. – Andbdrew Mar 31 '15 at 22:56
  • Duplicate of http://stackoverflow.com/questions/4260048/c-function-defined-as-int-but-having-no-return-statement-in-the-body-still-compi – vbraun Mar 31 '15 at 22:57
  • @vbraun: not really. OP is not asking why this compiles, but why that actually does what it's supposed to do, which is even more funny; see my answer below. – Marcus Müller Mar 31 '15 at 22:58
  • Why it (accidentally) works is also explained in the answers to the question that I linked to. – vbraun Mar 31 '15 at 23:01

3 Answers3

4

Does anyone know why this works?

pure luck. In fact, C says that "forgetting" the return statement in a function with a non-void return type results in undefined behaviour, i.e. anything might happen, your program might crash, your house might burn down, your compiler might start its own instance of SkyNet...

The point here is that your compiler probably just does this as a means of being nice. I think it shouldn't. Try compiling with -Wall, you will see a lot more warnings.

On x86, this won't happen accidentially. The return value is typically stored in the CPU register %eax, and if you don't explicitely return the pointer, there's no reason it should be in that register. However, the last one to write to %eax in your function is malloc, and since the return value of malloc, the address of the newly allocated space, is the same as the return value you want to produce, namely the address of your new flowNetwork this happens to work. Shudder!

EDIT to be clearer: what I mean with "the compiler is nice" is that it does not scream into your face, telling you you've made a terrible mistake, not that it's magically return the right value.

Marcus Müller
  • 34,677
  • 4
  • 53
  • 94
  • 1
    It is not UB unless the calling code attempts to use the return value – M.M Mar 31 '15 at 23:02
  • @MattMcNabb: pretty sure it's already undefiined behaviour if you don't deal with the return value, because the compiler might do erasing optimization on the values it will never pass on. – Marcus Müller Mar 31 '15 at 23:07
  • 2
    See C99 or C11 6.9.1/12 "If the `}` that terminates a function is reached, and the value of the function call is used by the caller, the behavior is undefined." – M.M Mar 31 '15 at 23:11
  • I wonder how standard handles situation when return value is __not__ used. – myaut Mar 31 '15 at 23:14
  • 1
    @myaut that's the thing Matt and I are arguing about. – Marcus Müller Mar 31 '15 at 23:16
  • Note that it's also possible that whatever is in `eax` when `initFlowNetwork()` returns is *not* the value that was returned by `malloc()`, but whatever scribbling on memory that occurs in the rest of `createGraph()` when using that bogus pointer just happens to seem to work without problem. Finally, it's highly unlikely that the compiler is doing any of this "to be nice". Or on purpose at all. In fact, it would be preferable if the compiler specifically made this *not* work well when compiling in a debug mode. – Michael Burr Mar 31 '15 at 23:17
  • You're arguing with 6.9.1/12 , not me :) – M.M Mar 31 '15 at 23:27
  • @MattMcNabb Yes, it seems I am :) – Marcus Müller Mar 31 '15 at 23:28
  • my gcc compiler, on linux, with parameters -Wall -Wextra -pedantic give a warning message about non void function having no return value statement. So the code does not compile cleanly. Another case where enabling all the compiler warnings would have saved lots of effort – user3629249 Mar 31 '15 at 23:42
  • As far as I know, the standard says nothing about not using the return statement in a function, it only defines what the return statement does, and its requirements. However, it does forbid using a return statement without a value in a non-void function. – teppic Mar 31 '15 at 23:50
  • @MattMcNabb I just looked at some generated assembly, and it appears that without a return statement in a non-void function gcc "corrects" it by not trying to return anything at all, as the stack is unaffected (as if the function were void). That of course would mean that if the return value is used, it would be accessing some random stack value, which is inline with the standard. – teppic Apr 01 '15 at 00:17
0

You are returning whatever is on the stack which could be a pointer to some useable memory that you're trashing with unpredictable side effects.

Solution: Don't do that.

clearlight
  • 12,255
  • 11
  • 57
  • 75
0

Whenever there is a function which is supposed to return something and it contains a local variable.Local variable occupies the top position in the function call stack.When such a function return without the return statement value on the top of the stack is poped.In your case it is N. Refer to this link

Community
  • 1
  • 1
avinash pandey
  • 1,321
  • 2
  • 11
  • 15