在我的大学里,我被告知所有内容都放在"堆栈的顶部。使用函数时。因此,当从一个返回时,堆栈的顶部被移除,直到我们到达底部 - 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元素都打印出来,意味着它的成功。
答案 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)。