【问题标题】:Why isn't the local variable going out of scope? [duplicate]为什么局部变量没有超出范围? [复制]
【发布时间】:2021-01-17 12:58:14
【问题描述】:

x 是局部变量,在执行 fun() 后应该超出范围 结束了。
它的地址是通过返回的指针和全局指针p 提供的,它指向不再有效的东西。但是,打印的输出仍然是5
为什么?

#include <stdio.h> 

int *p = NULL;

int *fun() { 
    int x = 5;  
    p = &x;
    return p; 
} 

// Driver Code 
int main() { 
    printf("%d", *(fun())); 
    return 0; 
} 

【问题讨论】:

  • 这是一种未定义的行为。
  • p 指向一个内存位置。该内存位置尚未被覆盖。
  • 嗨,史蒂夫,我对您的帖子进行了一些编辑,有点忘乎所以。我仍然认为它描述了您显示的代码,这就是您的意思。请仔细检查。如果我破坏了你的意思,请接受我的道歉并随时撤消。
  • 用 -Werror -O3 (-O2 or -Os) 编译然后你的问题就解决了。不会编译:) godbolt.org/z/ssEE7P
  • @Yunnosch 如果有人说 UB 有效,那他就没有希望了 :)。

标签: c global local-variables


【解决方案1】:

超出范围。
仍然访问它是无效的并导致未定义的行为。
仅仅因为您碰巧从那里读取了“可识别”值并不意味着它没有超出范围。

任何事情,包括但不限于使用局部变量调用另一个函数都可能/将改变您在那里读取的值。然而,这并不意味着只要你不做这些事情,你就可以使用/读/写。

【讨论】:

    【解决方案2】:

    我不知道编译器的实现细节,但我猜是因为保存 x 的堆栈空间仍然没有被其他一些变量擦除,因为你只调用了这个函数 fun() 一次。

    请比较以下两段代码

    1. 以下代码输出 5\n4\n

    在此代码中,每个printf 调用都会将函数结果刷新到屏幕上。同样,存放x的栈空间仍然没有被其他一些变量擦除,所以可以看到打印出来的变量值。

    #include<stdio.h>
    int *p = NULL;
    int *fun(int y)
    {
        int x = y;
        p= &x;
        return p;
    }
    
    // Driver Code 
    int main()
    {
        printf("%d\n", *(fun(5)));
        printf("%d\n", *(fun(4)));
        return 0;
    }
    
    1. 以下代码输出 4\n168558721\n

    在此代码中,您可以将第 15 行更改为首先打印 *r。你仍然会得到 4。你永远不会得到 5。当执行从第 14 行到第 15 行的翻译指令时,指针 q 和指针 r 必须指向同一个空间。由于我们调用的是同一个函数,所以第 13 行和第 14 行的栈帧应该有相同的结构,因此 x 分配在同一个地址下。

    #include<stdio.h>
    int *p = NULL;
    int *fun(int y)
    {
        int x = y;
        p= &x;
        return p;
    }
    
    // Driver Code 
    int main()
    {
        int *q = fun(5); // line 13
        int *r = fun(4); // line 14
        printf("%d\n", *q); // line 15
        printf("%d\n", *r);
        return 0;
    } 
    

    【讨论】:

    • 你能把贴出的代码中的行号去掉吗?
    • @chqrlie 完成。不错的建议。它将帮助人们粘贴和运行。 :)
    【解决方案3】:

    局部变量在堆栈中分配。当您从 main() 调用 fun() 时,堆栈显示为:

    +---------------+ <---- Stack pointer  
    |   local var x |  
    +---------------+ <---- Address of 'x'  
    | Return addr   |  
    |  in main()    |  
    +---------------+  
    |Local vars of  |  
    |   main()      |  
    +---------------+  
    |     ...       |  
    +---------------+  
    

    当你回到main()时,局部变量、返回地址和参数从堆栈中弹出。但是堆栈没有被清除(顺便说一下,它会消耗太多的CPU!)。所以,只有堆栈指针移动:

    +---------------+
    |   local var x |
    +---------------+ <---- Address of 'x'
    | Return addr   |
    |  in main()    |
    +---------------+ <---- Stack pointer moved with the pops
    |Local vars of  |
    |   main()      |
    +---------------+
    |     ...       |
    +---------------+
    

    堆栈指针上方的所有内容都被认为是无效的,即使它没有被清除。所以,这就是为什么你有幸在 main() 函数中获得了x 的值。

    但是假设你在 fun() 之后调用了另一个函数:

    #include<stdio.h> 
    #include<string.h> 
    
    int *p = NULL;
    
    void fun2()
    {
      int var = 18;
      int var2 = 43;
    
      printf("fun2() called, var@%p=%d, var2@%p=%d\n", &var, var, &var2, var2);
    }
    
    int *fun() 
    { 
      int x = 5;  
      p= &x;
      return p; 
    } 
    
    // Driver Code 
    int main(int argc, char *argv[]) 
    { 
      int *px;
    
      px = fun();
      printf("x@%p=%d\n", px, *px);
      if (argc != 1) {
        fun2();
      }
      printf("x@%p=%d\n", px, *px); 
      return 0; 
    } 
    

    当程序没有调用fun2()时,它的行为和你的一样,但我添加了x地址的显示:

    $ gcc try.c -o try
    $ ./try
    x@0x7ffd5beb5f04=5
    

    当程序传递任何参数时,我们在 fun() 之后调用 fun2() 并在调用 fun2 之前和之后显示 x ():

    $ ./try any_param
    x@0x7ffeadacc084=5
    fun2() called, var@0x7ffeadacc080=18, var2@0x7ffeadacc084=43
    x@0x7ffeadacc084=43
    

    我们可以看到在调用fun2()之后x的值变成了43,因为fun2()中的局部变量var2 fun() 运行时已与x 放在同一位置。因此,堆栈中的相同地址0x7ffeadacc084,当然还有x 的新值43,这实际上是var2 的值。

    下面是调用fun2()后栈的样子(_fun()之前的数据已经被fun2()的数据覆盖了):

    +---------------+
    |local var var  |
    +---------------+ <---- Address of 'var' = 0x7ffeadacc080
    |local var var2 |
    +---------------+ <---- Address of 'var2' = 0x7ffeadacc084
    | Return addr   |
    |  in main()    |
    +---------------+ <---- Stack pointer moved with the pops
    |Local vars of  |
    |   main()      |
    +---------------+
    |     ...       |
    +---------------+
    

    PS:堆栈从高地址向低地址增长。因此,var 的地址低于var2 的地址。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-01-13
      • 1970-01-01
      • 1970-01-01
      • 2016-08-19
      • 2016-09-05
      • 1970-01-01
      • 2014-05-02
      相关资源
      最近更新 更多