C ++。专访。关于返回int *值问题的范围

时间:2018-03-12 08:19:23

标签: c++ scope heap-memory

只是为了澄清,这是一个亚马逊的访谈问题。因此具有建设性!

这是我正在审核的网站。 问题是以函数的形式附加2个数组。

// a1(size1) and a2(size2) are arrays and you
// have to append them.
int* Append(int* a1,int* a2,int size1,int size2) 

我的问题是。从下面的代码中,为什么数组a实际上在函数Append中,一旦你离开函数,这个数组的范围就结束了?

#include <iostream>
#include <string>
using namespace std;


int* Append(int* a1,int* a2,int size1,int size2)
{
    int a[size1+size2],i;

    for(i=0;i<size1;i++)
    {
         a[i]=a1[i];
    }

    for(i=0;i<size2;i++)
    {
         a[i+size1]=a2[i];
    }

    return a;
}

int main()
{
  int a[] = {1,2,3};
  int b[] = {4,5,6,7};
  int* c;
  c = Append(a,b,3,4);
  for(i=0;i<7;i++)
     cout << c[i] << endl;
  cout << "abc";
}

&#34;我告诉他上面的代码确实失败了。他问我为什么失败了。我这样回答他。数组a实际上在函数Append中,一旦你离开函数,这个数组的范围就结束了。他告诉我怎么做。我不知道。后来他告诉我,我们必须使用malloc.Later为数组分配内存,他解释了我使用堆内存的工作原理。&#34;

  1. 我的问题是。从上面的代码中,为什么数组a实际上在函数Append中,一旦你离开函数,这个数组的范围就结束了?

  2. 我的第二个问题是,这与堆和堆内存有关吗?

  3. Heap和堆栈内存如何在Java中运行? Java中的堆和堆栈与C ++有何不同? (我用Java编写更多程序,所以我想了解更多关于Java中的内存的信息)

  4. 请附上有关该资料的任何链接/网址,我想了解更多。

2 个答案:

答案 0 :(得分:1)

1:是的,这是正确的。这通常也会引发编译器警告你是&#34;返回临时&#34;或类似的东西。

2:是的。您在函数范围内声明的所有变量都将放在堆栈中,并在范围(函数)结束时自动销毁。为了使它持续存在,直到你不告诉它(使用删除/免费),你必须使用new或malloc分配内存。

3:我只写了很少的Java,所以我无法完全回答这个问题,但是当用Java编程时,你不必考虑堆栈与堆内存(尽管它可能是有益的)知道它是如何工作的。 Java有一个&#34;垃圾收集器&#34;在后台运行,清除它为你分配的任何堆内存(除了基元之外的所有变量如int,float等在Java中分配在堆上),当它不再需要时。这当然会带来性能成本。

在这里,您还可以看到在C ++中处理内存的不同方法的简短摘要:) Proper stack and heap usage in C++?

编辑1:我现在注意到这个程序甚至没有编译的方式。 在主要功能;如果没有首先将i声明为整数(或者他们想要的任何类型),那么有一个for循环可以使i = 0。

编辑2:还;正如另一位评论者指出的那样; int [size1 + size2]不合法。因为它是合法的; size1和size2必须是const非参数变量,否则必须在堆上分配。

答案 1 :(得分:1)

C ++支持C所做的基本数据结构,例如简单数组,但这并不意味着鼓励它这样做。在C ++中,通常最好不要编写基本数组,而是使用std::vector类或类似的数据结构。

话虽如此,问题是一个面试问题,所以即使不鼓励这种方法,一般来说,让我们看一下函数,以及数组的初始化和分配。

首先要注意的是代码无法编译。 main函数包含一个循环,其循环变量i未声明。这是一件小事,我们可以快速解决它。编译器输出:

prog.cpp: In function ‘int main()’:
prog.cpp:29:7: error: ‘i’ was not declared in this scope
   for(i=0;i<7;i++)
       ^

一旦我们修复了编译器错误,我们就可以运行代码了,但是编译器仍然会提供一些警告,我强烈建议你看一下:

prog.cpp: In function ‘int* Append(int*, int*, int, int)’:
prog.cpp:8:9: warning: address of local variable ‘a’ returned [-Wreturn- local-addr]
     int a[size1+size2],i;
         ^

警告包含我们此时需要的所有信息。 (我知道在接受采访时你可能无法访问编译器来查看这些内容,但是当你回到家并想知道为什么得到你的回复时它们会很有帮助。)

警告基本上表示功能正在返回局部变量的地址。局部变量一旦离开作用域就会被释放,这就是堆栈的工作方式。当我们声明变量时,我们推送到堆栈,一旦声明了变量声明的范围,我们从范围中弹出变量(或者更确切地说是从变量中分配的内存)。

因此局部变量a在堆栈函数的开头声明,一旦函数返回,返回该局部变量的地址,但释放变量保留的内存。 / p>

所以是的,这与堆和堆栈有关,因为堆栈中的元素是活动的,而它们被声明的范围是活着的,而堆上的元素保持活动状态,直到它们被明确告知不再是活着(使用deletefree)。

当您被告知需要在堆上分配数组时,您可以使用new[]运算符执行此操作:

int* a = new a[size1 + size2];

请记住,必须使用new[]取消分配使用delete[]分配的内容。我不同意使用malloc,但这可能是代码的一些要求,在某些时候会使用free释放代码,此时您别无选择,只能使用malloc

我个人也不会这样做,我会使用std::vector代替,为什么要担心这个问题,当它可以如此简单:std::vector<int> a?作为一个面试问题,它被要求测试你的核心语言理解,虽然能够回答如何在堆上分配数组会很好,但它会显示出更多的力量来反映代码,并推荐一种替代的,更好的方法。例如,不使用new / malloc

通常,C ++返回值并按值解析参数(当我们不使用 by reference 选项时)。这意味着我们返回我们返回的值的副本。这就是我们如何能够返回简单的整数,或双精度,甚至std::string(我们忽略移动构造函数,以及它们如何简化工作),因为我们复制它们并返回。实际上我们也会复制变量a并返回它。所以你可能想知道,当我们复制a时,为什么它会失败。但正如我们已经建立的那样,从编译器警告中,我们实际返回的值是a的地址。

当我们在C ++(或C)中声明一个数组时,我们在内存中分配一个区域,该区域足够大以容纳所请求的元素数量。然后,变量将是指向第一个元素的地址的指针。这是我们实际复制的地址,而不是它指向的实际区域。因此该函数将返回指向不再分配的区域的指针。局部变量a不再存在,但a的内容被复制,并由函数返回。

Java是比C ++更高级的语言,并且允许您以相同的方式操作内存。在Java中,所有非原始的(intdouble,...)都保留在堆上。当不再引用Java对象时,它们将被释放。