为什么返回指向局部声明变量的指针为空而不是指向堆栈中位置的指针?

Why returning pointer to locally declated variable is null in place of pointer to location in stack?

在下面的代码片段中,str_s 不应指向堆栈中的某个位置。

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

char* fun_s(){
    char str[8]="vikash";
    printf("s :%p\n", str);
    return str;
}

char* fun_h(){
    char* str = (char*)malloc(8);
    printf("h :%p\n", str);
    strcpy(str, "vikash");
    return str;
}

int main(){
    char* str_s = fun_s();
    char* str_h = fun_h();
    printf("s :%p\nh :%p\n", str_s, str_h);
    return 0;
}

我知道 fun_s 的 return 有问题,这个指针的内容不可信,但根据我的理解,它应该指向堆栈中的某个位置而不是零? 我在控制台中得到以下输出。你能解释一下为什么第三行打印 (nil) 而不是 0x7ffce7561220

s :0x7ffce7561220
h :0x55c49538d670
s :(nil)
h :0x55c49538d670

GCC 版本

gcc (Ubuntu 7.4.0-1ubuntu1~18.04.1) 7.4.0
Copyright (C) 2017 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

OS :Ubuntu 18.04.3 LTS

大多数现代编译器检测 return 指向局部变量的指针,实际上 return NULL(一般来说,对于 UB 更积极,方法是 - 让程序尽快失败或 "make the UB possible to detect runtime" 在这种情况下)https://godbolt.org/z/pDUXmm

您的编译器故意从该函数中注入空 return 值。我没有可用的 gcc 7.4,但我有 7.3,我认为结果类似:

fun_s 编译为程序集可提供以下内容:

.LC0:
        .string "s :%p\n"
fun_s:
        push    rbp
        mov     rbp, rsp
        sub     rsp, 16
        movabs  rax, 114844764957046
        mov     QWORD PTR [rbp-8], rax
        lea     rax, [rbp-8]
        mov     rsi, rax
        mov     edi, OFFSET FLAT:.LC0
        mov     eax, 0
        call    printf
        mov     eax, 0 ; ======= HERE =========
        leave
        ret

注意 eax 的硬设置为零,当 return 返回给调用者时,它将保存结果指针。

str 设为静态可提供以下内容:

.LC0:
        .string "s :%p\n"
fun_s:
        push    rbp
        mov     rbp, rsp
        mov     esi, OFFSET FLAT:str.2943
        mov     edi, OFFSET FLAT:.LC0
        mov     eax, 0
        call    printf
        mov     eax, OFFSET FLAT:str.2943
        pop     rbp
        ret

简而言之,您的编译器正在检测本地地址 return 并将其重写为 NULL。这样做可以防止以后对上述地址的任何恶意使用(例如:内容注入攻击)。

我看不出有什么理由允许编译器这样做。我相信语言纯粹主义者会证实或拒绝这种怀疑。