为什么返回的堆栈指针被 gcc 替换为空指针?

Why is a returned stack-pointer replaced by a null-pointer by gcc?

我在 c 中创建了以下函数作为 demonstration/small 关于堆栈在 c 中如何工作的谜语:

#include "stdio.h"

int* func(int i)
{
    int j = 3;
    j += i;
    return &j;
}

int main()
{
    int *tmp = func(4);
    printf("%d\n", *tmp);
    func(5);
    printf("%d\n", *tmp);
}

这显然是未定义的行为,编译器也会对此发出警告。然而不幸的是,编译并没有完全成功。由于某种原因,gcc 将返回的指针替换为 NULL(参见第 6d6 行)。

00000000000006aa <func>:
 6aa:   55                      push   %rbp
 6ab:   48 89 e5                mov    %rsp,%rbp
 6ae:   48 83 ec 20             sub    [=11=]x20,%rsp
 6b2:   89 7d ec                mov    %edi,-0x14(%rbp)
 6b5:   64 48 8b 04 25 28 00    mov    %fs:0x28,%rax
 6bc:   00 00 
 6be:   48 89 45 f8             mov    %rax,-0x8(%rbp)
 6c2:   31 c0                   xor    %eax,%eax
 6c4:   c7 45 f4 03 00 00 00    movl   [=11=]x3,-0xc(%rbp)
 6cb:   8b 55 f4                mov    -0xc(%rbp),%edx
 6ce:   8b 45 ec                mov    -0x14(%rbp),%eax
 6d1:   01 d0                   add    %edx,%eax
 6d3:   89 45 f4                mov    %eax,-0xc(%rbp)
 6d6:   b8 00 00 00 00          mov    [=11=]x0,%eax
 6db:   48 8b 4d f8             mov    -0x8(%rbp),%rcx
 6df:   64 48 33 0c 25 28 00    xor    %fs:0x28,%rcx
 6e6:   00 00 
 6e8:   74 05                   je     6ef <func+0x45>
 6ea:   e8 81 fe ff ff          callq  570 <__stack_chk_fail@plt>
 6ef:   c9                      leaveq 
 6f0:   c3                      retq   

这是用 gcc 版本 7.5.0 和 -O0-flag 编译的二进制文件的反汇编;没有使用其他标志。这种行为使整个代码毫无意义,因为它应该显示堆栈在函数调用中的行为方式。有什么方法可以使用至少是最新版本的 gcc 来更直接地编译此代码?

出于好奇:首先像这样更改代码的行为有什么意义?

将 return 值放入指针变量似乎会改变编译器的行为,它会生成 return 指向堆栈的指针的汇编代码:

int* func(int i) {
     int j = 3;
     j += i;
     int *p = &j;
     return p;
}