【问题标题】:Return a pointer that points to a local variable [duplicate]返回指向局部变量的指针[重复]
【发布时间】:2016-10-25 04:32:10
【问题描述】:

我不知道为什么会这样。由于 x 是一个局部变量,我想当我尝试返回它时会出错。但是,第一个 printf 工作正常,但它只打印出 0。谁能解释这里发生了什么?

#include <stdio.h>

int* func1() {
    int x = 123123;
    int *y = &x;
    return y;
}

int main()
{
    int* c = func1();
    printf("%d\n", *c); // output: 123123
    printf("%d\n", *c); // output: 0
    return 0;
}

【问题讨论】:

  • 在使用局部变量所在的空间时没有发生任何事情。然后,发生了一些事情。
  • 什么意思? 0.o
  • 行为未定义。您正在引用堆栈上对您不可用的存储。在其他东西触及它之前,它可能仍然保留早期函数调用的值,但你不能指望它。显然,您对printf 的调用有时会覆盖该存储。这是一个错误。只需修复它并继续生活。
  • c 的分配是 UB。
  • 这是个好问题。变量的范围似乎存在问题。不要将局部变量地址返回给其他函数。因为局部变量的范围有限,一旦函数调用完成,它将破坏内存或分配给其他人。

标签: c


【解决方案1】:

会发生以下情况:

  1. func1 中,您创建了本地x 变量并用一个值对其进行了初始化,即x 现在在堆栈中。
  2. 你得到x的地址并返回给main
  3. 返回时,func1 及其变量x(以及与问题无关的y)是freed,或者从堆栈中弹出,即它们的内存位置不再保留以保存他们的价值观。此后,程序的任何其他部分都可以使用在func1 中为x 分配的内存空间,因为func1 不再处于活动状态。
  4. 您的第一次printf 调用仍然碰巧看到x 曾经所在的内存位置的旧值(但这不能保证)并且
  5. 第二个printf 调用表明其他东西(值为0,例如第一个printf 的返回值为described by R. Joiny)正在(或曾经)使用与@987654339 相同的地址@在func1内。

This article of the C Programming Boot Camp 几乎描述了你的情况:

理解堆栈的一个关键是,当一个函数 退出,它的所有变量都从堆栈中弹出(因此 永远失去)。因此堆栈变量本质上是局部的。这是 与我们之前看到的称为变量作用域或本地的概念有关 vs 全局变量。 C 编程中的一个常见错误是试图 访问在某个函数内部的堆栈上创建的变量, 从您的程序中该功能之外的位置(即在那之后 函数已退出)。

【讨论】:

    【解决方案2】:

    printf 基本上是一个函数。喜欢你的func1

    函数使用一些 RAM,您的工作区用于局部变量。如果您离开该功能,它会被分配为“清除使用”(未删除!)。

    因为第一个printf() 直接出现在func1() 函数之后,所以局部变量C 仍然存在,因为它还没有被覆盖。所以这行得通。但是,如果您查看此MAN page,您会看到, printf 具有 int 作为返回值。所以必须将其写入某个地方,那么您的 PC 会做什么?当然,将其写入分配给您的程序的 RAM 的第一个空闲地址。你有你的零。

    请务必注意,没有其他程序可以访问您的 RAM,Windows 会自动为每个进程保留 RAM,因此它必须是来自 printf()(或 printf 在执行时使用的其他一些局部变量)的返回值。

    【讨论】:

      【解决方案3】:

      由于 x 是一个局部变量,我以为我尝试返回它时会出错。

      对这个问题的更一般的回答是:C 代码中的错误代码不一定会产生编译器或运行时错误。

      如果你很幸运,你会得到一个编译器错误或警告。

      如果你幸运的话,当错误的代码运行时,你会遇到运行时崩溃(这通常发生在空指针错误中)。使用调试器很容易追踪到这一点。

      但是,如果您不走运,错误的代码可能会在以后导致崩溃(在看似无关的代码中),或者默默地破坏某些数据结构(使您的程序以奇怪的方式运行而不会崩溃),甚至看起来可以正常工作(直到你在别处添加看似无害的代码,或使用不同的编译器,或同一编译器的不同版本,或只是不同的编译选项)。


      从技术上讲,您的代码在这一行有未定义的行为:

      int* c = func1();
      

      您正在使用func1 的返回值(将其写入c)。但该值是func1 中局部变量的地址,在func 返回时已不存在。这意味着返回值是一个无效的指针,仅仅使用这样的指针会导致未定义的行为(你甚至不必取消引用它)。

      printf("%d\n", *c); // output: 123123
      

      好吧,这一行既使用了错误指针(从c 读取它)又取消了它的引用。该程序似乎仍然可以正常工作。

      printf("%d\n", *c); // output: 0
      

      看似无害的添加,但此行不会产生预期的输出。现在看起来像是静默数据损坏。

      请注意,这些都不能保证。使用不同优化设置的不同编译器或相同编译器很可能会生成行为不同的代码。就标准而言,编译器必须生成与 C 代码的可观察行为相匹配的代码。但是行为未定义的 C 代码可能导致任何事情发生;对于编译器可以用它做什么没有任何保证或限制。

      【讨论】:

      • 这真的是编译器的事情,而不是操作系统的事情,会发生什么?我对编译器了解不多,但我认为操作系统通过在 RAM 上分配所需的内存来赋予指针它的值......
      猜你喜欢
      • 2023-04-09
      • 1970-01-01
      • 2014-05-14
      • 2020-10-13
      • 1970-01-01
      • 2021-11-11
      • 2021-02-21
      相关资源
      最近更新 更多