10

Why does the following ends up with no error?

void func()
{
   func();
}

int main()
{
   func();
}
Ant
  • 123
  • 3
  • Does the application just hang, or does it end with back at the command prompt with no remark? The correctness of the answers you gotten likely depends on you answer to this question. – Ira Baxter Mar 30 '11 at 23:41
  • The output says: `Press any key to continue . . .` – Ant Mar 30 '11 at 23:56
  • So it is some kind of crash, rather than a continuous loop consuming CPU? – Ira Baxter Mar 31 '11 at 02:16
  • What happens when you do press a key? – vbence Mar 31 '11 at 07:17
  • @vbence It exits and closes the console window. – Ant Mar 31 '11 at 19:14
  • This suggests that it is definitely not an infinite loop. That way the program would never exit. You have to disassemble the resulting code. I can not recommend you a program for that without knowing the OS and architecture you are compiling to. – vbence Mar 31 '11 at 22:27
  • Related: http://stackoverflow.com/questions/2178115/are-compilers-allowed-to-eliminate-infinite-loops/2181045#2181045 – anatolyg Apr 03 '11 at 16:55
  • @Ant: Which OS, which C++ compiler? – Ira Baxter Apr 03 '11 at 17:00
  • "This suggests it is not an infinite loop". Agreed. I think most of the answers here suggesting that is a compiler optimization into an infinite loop, in spite of their upvotes, are wrong. – Ira Baxter Apr 03 '11 at 17:02

6 Answers6

24

In theory, it would overflow the stack (because, even if no local variables are used, each call would add the previous return address on the stack); in practice, with optimizations enabled, it doesn't overflow because of tail call optimization, which actually avoids any resource consumption transforming the call in a jump, thus not consuming the stack.

This can be easily seen by examining the optimized assembly generated by the OP code:

func():
.L2:
        jmp     .L2
main:
.L4:
        jmp     .L4

func is optimized to an infinite loop, both the "freestanding version" and the inlined call in main.

Notice that this is coherent with the C++ standard for the "as if" rule: the compiled program must run as if it were what you requested in the code (in terms of effect), and since the stack size is just an implementation limit, the generated code that uses a call and the one that uses a jmp are equivalent.

But: this is an even more particular case, as the standard even says that infinite looping (defined as "not terminating and not having some side-effect") is actually undefined behavior, so in theory the compiler would be allowed to omit that call entirely.

Community
  • 1
  • 1
Matteo Italia
  • 123,740
  • 17
  • 206
  • 299
8

Likely, your compiler optimized it away and turned it into a while(true){} construct.

Puppy
  • 144,682
  • 38
  • 256
  • 465
5

It does end with a Segmentation fault on my Linux system - Valgrind indicates a possible stack overflow, which is of course true, since for each function call a new stack frame is required.

However, enabling optimisations in the compiler reduces this whole program to an infinite loop, which, naturally, does not end at all:

        .file   "so.c"
        .text
        .p2align 4,,15
.globl func
        .type   func, @function
func:
.LFB0:
        .cfi_startproc
        .p2align 4,,10
        .p2align 3
.L2:
        jmp     .L2
        .cfi_endproc
.LFE0:
        .size   func, .-func
        .p2align 4,,15
.globl main
        .type   main, @function
main:
.LFB1:
        .cfi_startproc
        .p2align 4,,10
        .p2align 3
.L5:
        jmp     .L5
        .cfi_endproc
.LFE1:
        .size   main, .-main
        .ident  "GCC: (GNU) 4.4.3"
        .section        .note.GNU-stack,"",@progbits

Here's the interesting part:

.L5:
        jmp     .L5
thkala
  • 84,049
  • 23
  • 157
  • 201
3

If you are compiling and running this on Windows in a command window, you may get a crash but without any remarks from the OS. (We build a funny compiler and run into this problem a lot). Microsoft's claim is that when program does very bad things, they can't recover... so they simply kill the process and restart the command prompt. LIkely in your case, after you've recursed to the stack limit, when the trap handler attempts to do something (like push trap status on the stack) there isn't any space and Windows kills your process.

I personally think this is inexcusable behavior. If my process does something bad, the OS should always complain. It might say, "process terminated with prejudice", along with some kind of indication ("you ran out of stack in the last-ditch error handler") but it should say something.

Multics got this right in 1966. Its a shame we haven't applied these lessons in over 40 years.

Community
  • 1
  • 1
Ira Baxter
  • 93,541
  • 22
  • 172
  • 341
1

On my machine it ends with a segfault (like infinite recursion should).

Maybe your shell isn't reporting the segfault. What OS are you using?

jbruni
  • 1,238
  • 8
  • 12
1

Back in the olden days, when you wanted to over-optimize an ASM program there was a practice: may times a function ended with calling an other function (then returning). It would look something like:

somefunc:

    ; do some things

    CALL someotherfunc
    RET

someotherfunc:

    ; do some other things

    RET

This way when CALL someotherfunc happened the address of the next instruction (the RET) is saved into the stack and then someotherfunc returned just to execute the a return. Exactly the same results can be achieved with a JMP to someotherfunc. This way the stack will not contain the address of the last instruction, but it will contain the original caller's address. So when someotherfunc makes it's RET the program will continue at the original caller.

So the optimized code would look like:

somefunc:

    ; do some things

    JMP someotherfunc

someotherfunc:

    ; do some other things

    RET

And if somefunc is calling itself as the last instruction (in fact that is the only instruction), it would indeed look like:

somefunc:

    JMP somefunc
BenMorel
  • 34,448
  • 50
  • 182
  • 322
vbence
  • 20,084
  • 9
  • 69
  • 118