棘手的C练习与指针有关。需要帮助来理解解决方案

时间:2014-10-30 04:07:43

标签: c

请考虑以下代码:

int *foo (int x) {
int a[2];
a[0] = x;
a[1] = x + 1;
return a;
}
…
int *p1 = foo(2);
int *p2 = foo(3);

在此代码段结束时,以下各项的值是什么? (给出答案)

 p1[0] = 3
 p1[1] = 4
 p2[0] = 3
 p2[1] = 4

由于在堆栈上分配了a,因此在调用返回时内存不会保持分配状态,并且可能会被其他内容重用。在这种情况下,由于foo(3)在foo(2)之后立即在相同的调用深度处调用(即它们使用相同的堆栈空间),因此它们都将返回相同的指针 - 即p1等于p。

我不明白上面的解释。它到底意味着什么?为什么我们对p2和p1有完全相同的值?我知道你不能在C中返回指向局部变量的指针。但我不明白为什么p2和p1具有相同的值....

5 个答案:

答案 0 :(得分:7)

这是未定义的行为,您的程序也可能崩溃。

局部变量存储在堆栈中,其生命周期仅在函数范围内。

在您的情况下,程序下次重用相同的位置,因此您的值将被覆盖,但它并不总是相同,并且永远不会返回局部变量的地址。

答案 1 :(得分:7)

使用指向其范围之外的自动存储变量(作用于函数的变量)的指针是未定义的,这正是从不执行此操作的原因,并且从不依赖于它的行为。

但是,如果你想剥离编译器/机器/操作系统上的封面,原因是自动存储恰好在两个函数调用的同一地址分配。

一个例子......

#include "stdio.h"

int* foo(int x) {
  int a[2];

  printf("&a[0] = %p\n", &a[0]);
  printf("&a[1] = %p\n\n", &a[1]);

  a[0] = x;
  a[1] = x + 1;

  return a;
}

int main(int argc, char* argv[]) {

  printf("foo(2)\n");
  int* p1 = foo(2);

  printf("foo(3)\n");
  int* p2 = foo(3);

  printf("p1[0] = %i\n", p1[0]);
  printf("p1[1] = %i\n\n", p1[1]);

  printf("p2[0] = %i\n", p2[0]);
  printf("p2[1] = %i\n", p2[1]);

  return 0;
}

...输出

foo(2)
&a[0] = 0x7fff4dd0f054
&a[1] = 0x7fff4dd0f058

foo(3)
&a[0] = 0x7fff4dd0f054
&a[1] = 0x7fff4dd0f058

p1[0] = 3
p1[1] = 4

p2[0] = 3
p2[1] = 4

因此,&a[0]&a[1]foo(2)foo(3)中都具有相同的地址。

当你输入一个函数时,它通常会创建一个堆栈帧(通过递减x86上的堆栈指针)。这会在堆栈上为自动存储变量分配内存。离开函数时,堆栈帧被销毁(堆栈指针返回其原始值)。因此,如果再次输入相同的函数,通常会对堆栈帧使用相同的内存(堆栈指针递减到与上次调用函数相同的值)。

答案 2 :(得分:1)

每个函数调用内存在堆栈上为函数变量分配,堆栈指针向前移动。函数执行后,由于效率原因,不会擦除堆栈内存,并且所有数据都保留在那里。因此,如果您第二次调用相同的函数并保留一些未初始化的变量,您可以在上一次函数调用中找到一些有趣的值。 C中的每个数组都存储为一大块内存,通过移位指针找到元素。 至于你的问题:foo返回指向整数的指针,这实际上是一个内存地址。 在foo(2)之后,p1将存储一些地址,例如0x00。将带有索引的大括号[]添加到整数指针意味着将整数大小*索引添加到内存地址。我们可以向指针添加任何随机索引并尝试从那里获取数据。如果我们幸运并且内存可读 - 我们将有一些垃圾数据。第一次调用函数后

p1 points to a stack array and values are:
p1[0] == 2;
p2[1] == 3;
p1 == 0x00; (for example)

执行函数并返回堆栈指针。下一个函数调用foo(3)在堆栈上获得相同的内存块。第二次调用使用新值重写变量。第二次通话后,我们得到:

p2[0] == 3;
p2[1] == 4;
p2 == 0x00; (same memory address)

问题是p1指向堆栈上的相同内存地址。如果你调用任何其他函数 - p1和p2都将再次更改,因为堆栈的相同区域将再次被重用。

答案 3 :(得分:0)

在你的函数foo()中你有一个局部变量,你正试图返回局部变量的地址。不要你的编译器抛出以下错误:

warning: function returns address of local variable

如果你必须得到你发布的答案,那么你需要使数组int a[2]全局。 否则我们在这里有一个未定义的行为。

答案 4 :(得分:0)

从函数调用返回时,堆栈展开将释放为该数组分配的内存。那个记忆不再可用。行为未定义。