返回指向局部变量的指针

时间:2016-06-23 02:43:42

标签: c

我不知道为什么会这样。由于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;
}

3 个答案:

答案 0 :(得分:5)

printf基本上是一个功能。与您的func1一样。

函数使用一些RAM,您的工作空间用于局部变量。如果你离开这个功能,就会变成像&#34;清楚使用&#34; (没有删除!)。

因为第一个printf()直接来自func1()函数,所以局部变量C仍然存在,因为它还没有被覆盖。这样才行。但是如果你看一下这个MAN page你可以看到,printf将int作为返回值。所以必须写到某个地方,那么你的PC会做什么?当然把它写到RAM的第一个免费地址,分配给你的程序。你有零。

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

答案 1 :(得分:4)

发生以下情况:

  1. func1内,您创建了本地x变量,并使用一个值初始化它,即x现在已在堆栈中。
  2. 您获得x的地址并将其返回main
  3. 返回时,func1及其变量x(与问题无关,y)为free d,或从堆栈中弹出,即其内存不再保留位置以保持其值。在此之后,允许程序的任何其他部分使用在x内为func1分配的内存空间,因为func1不再处于活动状态。
  4. 您的第一个printf电话仍然会看到x曾经存在的内存位置的旧值(但无法保证)和
  5. 第二个printf调用显示其他内容(值为0,例如第一个printf的返回值为described by R. Joiny)(或者在x内使用与func1相同的地址。
  6. This article of the C Programming Boot Camp几乎描述了你的情况:

      

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

答案 2 :(得分:4)

  

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

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

如果你很幸运,你会收到编译错误或警告。

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

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

从技术上讲,您的代码在此行中具有未定义的行为:

int* c = func1();

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

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

好吧,这一行都使用坏指针(从c读取)并取消引用它。该程序似乎仍然可以正常工作。

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

看似无害的添加,但这条线不会产生预期的输出。现在它看起来像是无声数据损坏。

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