2

我有两个相同程序的示例。这个程序有一个函数,它创建一个数组并返回指向数组的指针。

第一个程序(C):

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

#define N 5

int* GetValues()
{
   int items[N] = { 1, 2, 3, 4, 5 };
   return items;
}

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

    int *array = GetValues();    
    int i;    
    for(i = 0; i < N; i++)
    {
        printf("%d\n", array[i]);
    }    

    return (EXIT_SUCCESS);
}

第二个程序(Java):

package tests;

public class Tests {

    public static int[] foo() {        
        int array[] = { 1, 2, 3, 4, 5 };
        return array;        
    }

    public static void main(String[] args) {        
        int[] array = foo();        
        for(int i = 0; i < array.length; i++) {
            System.out.println(array[i]);
        }                
    }
}

Java程序的结果如下:1、2、3、4、5

C程序的结果如下: 1 -1075386156 -1218492432 1 -1216747208

为什么我们会有不同的结果?我的版本如下。

在 GetValues() 函数内部的 C 程序中,将创建并初始化 items[] 本地数组。返回指令将返回指向数组开头的指针,但该数组将分配在该函数的本地内存中。当调用 GetValues() 函数的最后一条指令时,本地内存将被销毁。在这种情况下,我们无法预测那里存储了哪些数据,并且我们不知道将打印什么printf("%d\n", array[i])指令(难怪,内存已被破坏并且值也已被破坏)。

在java程序中我们有以下情况。JAVA 中的数组是对象。java中的对象存储在堆中。因此,在执行foo()方法之后,array将创建对象并将其放入堆中。执行方法后局部变量会被清除,但是我们指向array-object的指针还在堆中(垃圾收集器会明白什么时候必须删除这个对象),为什么我们能够正常打印它。

我对吗?我是否正确理解了这些功能?如果没有,有人可以纠正我吗?提前致谢。

PS对不起我的英语我希望我或多或少清楚地解释了我的问题。

4

4 回答 4

3

是的,你是完全正确的。Java 中的数组存储在堆上并返回给调用者。java程序就像你写的那样:

int array[] = new int[5];
array[0] = 1;
...etc.
 return array;  

这没问题。

C 程序中的数组是函数的本地数组,当函数返回时,指向这些本地值的指针无效。

于 2013-05-30T19:08:15.890 回答
2

在 C 程序中,int* GetValues()您正在返回指向未定义行为的局部变量的指针,items一旦您从函数返回,该指针将不存在。这将是修复代码的一次方法:

int *items = malloc( sizeof(int) * 5 ) ;
items[0] = 1 ;
// initialize rest of array 
return items;

只要记住你需要free什么malloc

在 Java中,数组是对象,并且对象是围绕引用传递的,因此 Java 中不存在相同的问题,因为一旦对象不再具有对它的引用,Java 就会垃圾收集该对象。

于 2013-05-30T19:06:20.177 回答
1

我不确定您是在寻求帮助修复代码还是帮助理解为什么会出现问题。如果您需要后者的帮助,您似乎已经理解它:由于 C 数组存储在堆栈中(您称为本地函数内存),因此当函数返回时,数据不再存在于内存中。相反,您在释放后返回指向同一内存的指针。这与返回未初始化的指针本质上是一样的,这可能会导致 C 代码出现一些非常讨厌的问题!

int* GetValues()
{
    int items[N] = { 1, 2, 3, 4, 5 };
    return items;
}

如果您有兴趣修复上面的代码,则需要在返回之前为堆上的数组分配空间。堆比堆栈有更多的空间,只要你有一个指向特定块的指针,就可以从程序中的任何地方访问。

你会看到类似的东西:

int* GetValues(){
  int * items = malloc(sizeof(item) * N)
  //I used a for loop to populate the array, just for speed
  int i;
  for(i = 0; i < 4; i++){
    items[i] = i+1
  }
  return items
}

这可能并不完美,因为我很快就将它拼凑在一起,同时也在做其他事情,但希望它能传达这个想法。大图是您需要初始化一个指针以指向堆上内存中的一个块(使用 malloc 完成)。

如果 malloc 代码对您没有意义,您可能需要阅读 C 中的内存管理。这可能会很痛苦,但这也是指针如此有用的部分原因。

于 2013-05-30T19:17:34.097 回答
1

在 Java 中,数组是对象(引用自Java Language Specification):

对象是类实例或数组。

因此,在您的应用程序的 Java 版本中,方法中创建的数组foo实际上存储在堆上,而不是堆栈上。因此可以在foo方法之外访问它。

于 2013-05-30T19:05:55.583 回答