如何在函数外部访问函数变量?

时间:2017-03-04 08:58:37

标签: c function pointers memory dynamic-memory-allocation

在我的大学里,我被告知所有内容都放在"堆栈的顶部。使用函数时。因此,当从一个返回时,堆栈的顶部被移除,直到我们到达底部 - main()。建议一切都丢失了,我们已经完成了以前功能的本地范围。

大多数书都告诉我同样的事情。

但是,我遇到了很多事情,我使用了这个确切的功能。

例如:

void address (bool** xpp)
{
    bool* y = (bool*) malloc(10*sizeof(bool));
    y[2] = false;
    **xpp = &y;
}

int main(void)
{
    bool* x;
    bool** xp = &x;

    address(&xp);
    xp[0] = false;
    xp[2] = false;
    xp[7] = false;

    printf("%d", xp[0]);
    printf("%d", xp[2]);
    printf("%d", xp[7]);

    return 0;
}

在这种情况下,根据我的理解,我不应该在地址()之后引用main()中的xp []数组,因为,实际上我已经设置了它首先指向数组的指针元素指针,但在返回main()后,我在address()中创建的数组应该消失了。所以它应该指向无处并且应该弹出异常。

然而,所有bool xp元素都打印出来,意味着它的成功。

3 个答案:

答案 0 :(得分:2)

嗯,这里有两个不同的答案。你的问题归结为:“我听说局部变量在从堆栈中弹出时会丢失,但我尝试了,它们仍然在那里”,但你的实际代码证明了在堆栈上使用可能丢失的局部变量;它演示了malloc的使用,这是完全不同的。

然后第二个答案是,任何时候你问任何事情就像“我听说X不起作用,但我尝试了它,它起作用了”,你正在玩火:它可能似乎有效,但不能保证。

让我们看一下程序略有不同的版本。我只是让address函数返回指向数组第一个元素的指针,而不是使用指针指针。而不是一组bool,我将使用int数组,所以我可以更容易地显示有趣的数字。这是代码的第一个版本:

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

int *address()
{
    int *p = malloc(5 * sizeof(int));
    if(p == NULL) abort();
    p[0] = 1; p[1] = 2; p[2] = 3; p[3] = 4; p[4] = 5;
    return p;
}

int main()
{
    int *a = address();
    int i, sum = 0;
    for(i = 0; i < 5; i++) sum += a[i];
    printf("sum = %d\n", sum);
    for(i = 0; i < 5; i++) printf("a[%d] = %d\n", i, a[i]);
}

函数address返回一个指针,但它是一个指向动态分配内存的指针,即使在address返回后也能保证这种指针。程序打印

sum = 15
a[0] = 1
a[1] = 2
a[2] = 3
a[3] = 4
a[4] = 5

这正是你所期望的。

但现在让我们将函数address更改为不调用malloc,而是使用本地数组:

int *address()
{
    int la[5];
    la[0] = 1; la[1] = 2; la[2] = 3; la[3] = 4; la[4] = 5;
    return la;
}

这个功能坏了。它返回指向本地数组的第一个元素的指针。当函数返回其调用者时,该数组将不再存在。指针永远不会对其调用者有用;它实际上保证不起作用。实际上,当我编译第二个版本时,我的编译器警告我:

warning: address of stack memory associated with local variable 'a' returned

但是如果忽略那个警告并且无论如何都要运行生成的程序,这就是我得到的:

sum = 15
a[0] = 507402241
a[1] = -16764064
a[2] = 0
a[3] = 0
a[4] = 0

这是一个非常有趣的结果。要注意的第二件事是数组的内容都是错误的。我们没有看到1, 2, 3, 4, 5;我们看到一些显然是垃圾的数字。但这是可以预料到的,因为函数la中的数组address已经消失。

但第一个也许更有趣的事情是总和是正确的! 发生了什么?总和与打印的数组内容不匹配!就好像address函数返回的“已损坏”指针工作了一段时间,只需要足够长的时间来计算“正确”的总和,但后来被删除了。事实上,这正是发生的事情。

函数address返回后,数组la“消失”,但它正在使用的堆栈上的内存尚未被重用或擦除,因此位模式仍然存在。所以调用函数main可以尝试访问这些位,它甚至可以得到正确的答案 - 它计算相同的总和,15。重要的是要注意这绝对是不是< / em>保证工作;你从不想要在真正的程序中依赖它 - 它恰好可以工作。

但是,在计算总和后,main调用printf将其打印出来。 printf是一个被调用的函数 - 一个大而复杂的函数 - 它可以完成各种各样的事情,并在堆栈上分配各种自己的变量。这就是分配给la数组的堆栈内存实际被覆盖的时候。这就是为什么当main函数的后半部分试图打印出数组时,它就是垃圾。

还有另外一点要做,这与地址有关。当我们担心本地数组la丢失或丢失时,有两个问题要问:我们是否丢失了数组的内容,并且,我们是否丢失了指向数组的指针?在回答这些问题时,我们遇到了一个重要的事实:即使函数的本地(堆栈)存储已经消失,函数仍然可以很好地返回一个值。

要看到这一点,请考虑功能

int five()
{
    int r = 5;
    return r;
}

当函数five返回时,其局部变量r消失。但打电话的人说

int x = five();

没有问题,因为当函数five返回时,即使正在分配函数five的局部变量r,返回值5也会被复制到调用者自己的变量中x,所以它不会丢失。

但是所有这些都说 - 除了数组是一个本地(堆栈)数组还是用malloc分配的问题之外 - 还有一些错误,有点不同的错误,他的方式你正在接受y的地址。 y是一个局部变量,所以在函数退出后,&y是伪造的,无论你把它藏在哪里都无法保证工作。 (但是,再一次,它似乎可以工作一段时间,直到其他东西覆盖堆栈。)

答案 1 :(得分:0)

  

如何在函数外部访问函数变量?

假设它没有被宣布为static你根本就不能,因为你提到的原因:它已经离开了这个功能。

  

在返回main()后,我在address()中创建的数组应该消失了。

malloc的调用没有创建&#34;变量&#34;,但是它从堆中分配了内存。存储在函数变量y内的所有内容都是这个内存块的地址。当y离开函数时,这个地址确实会丢失。

为了不丢失分配的内存地址修复代码,例如:

#include <stdlib.h> /* for malloc() */
#include <stdio.h>  /* for printf() */

void address (bool** xpp)
{
  bool* y = malloc(10*sizeof(bool));
  y[2] = false;
  *xpp = y;
}

int main(void)
{
  bool* xp;

  address(&xp);
  xp[0] = false;
  xp[2] = false;
  xp[7] = false;

  printf("%d", xp[0]);
  printf("%d", xp[2]);
  printf("%d", xp[7]);

  return 0;
}

此代码仍存在一个主要缺陷,即调用者(main)和被调用者(address)没有#34;谈话&#34;关于需要分配多少内存。

答案 2 :(得分:0)

几乎关于此代码的所有内容都具有误导性。

问题是,&#34; xp指针在函数address返回后如何有效?但事实证明,对address的调用不会修改xp指针。

xp设置为指向x。拨打address的电话并没有改变。 (对address的调用最终会改变xp指向的内容,即更改x

虽然xp不是bool的指针,但在三个作业中使用它就好像它一样

xp[0] = false;
xp[2] = false;
xp[7] = false;

虽然xp实际上没有指向分配给10个bool的内存,但无论它指向何处(也就是围绕x的堆栈上的某个地方),显然有足够的记忆力来意外存放10个bool而不会造成太大的伤害。

然后,在任何地方存放了几个bool,试图将这三个bool打印出来(从任何地方开始)就好了。

关于在函数返回后如何仍然可以访问函数中的本地存储(虽然不正确)的问题的答案 - 尽管这段代码实际上并没有这样做 - 请参阅我的其他答案

为了记录,还有其他问题。电话

address(&xp);

是类型不匹配(&xpp是指向指针指向bool的指针,但是address()接受指针指向bool的指针。这条线

 **xpp = &y;

是类型不匹配(&y是指针指向= -bool,但**xpp是bool)。像

这样的行
xp[0] = false;

是类型不匹配(false是bool,但xp[0]是指向bool的指针)。最后,像

这样的行
printf("%d", xp[0]);

是类型不匹配(xp[0]是指针tobool,但%d需要int,或者bool)。